希望通过博客和大家相互交流,相互学习,如有错误,请评论区指正

文章目录

  • 一、什么是链表
  • 二、手撕链表
      • 属性定义
        • 结点对象
        • 构造方法
        • 链表对象
        • 构造方法
      • 链表的实现
        • 头插
        • 尾插
        • 得到单链表的长度
        • 任意位置插入
        • 查找是否包含关键字key
        • 删除第一次出现关键字为key的节点
        • 打印单链表
        • 清空链表
        • 删除所有值为key的节点
    • 2. 双向链表
      • 属性定义
        • 链表对象
        • 构造方法
      • 链表的实现
        • 头插
        • 尾插
        • 得到链表的长度
        • 任意位置插入
        • 查找是否包含关键字key
        • 删除第一次出现关键字为key的结点
        • 删除所有值为key的节点、
        • 打印链表
        • 清空链表,释放内存

一、什么是链表

顺序表在空间利用,系统消耗,插入元素方面都是存在缺陷的。而链表是最常用的动态存储方法,克服了顺序表的缺点。

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的

链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储指向下一个结点的引用域


链表的结构很多,有以下6种情况,组合起来有8种

  • 有头、无头
  • 单向、双向
  • 循环、非循环

在这8种情况中,我们一般重点学习其中的2种,无头单向非循环链表和无头双向循环链表

二、手撕链表

这里实现无头单向非循环链表

属性定义

我们需要有链表结点和链表对象,如下:

结点对象

public class Node {public int data;public Node next;
}

构造方法

public Node(int data) {this.data = data;    // 注意要用 this, 里面直接写data的话就是形参datathis.next = null;
}

链表对象

public class MyLinkedList {public Node head;
}

构造方法

public MyLinkedList() {this.head = null;
}

链表的实现

//头插
public void addFirst(int data);
//尾插
public void addLast(int data);
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data);
//查找是否包含关键字key
public boolean contains(int key);
//删除第一次出现关键字为key的节点
public void remove(int key);
//删除所有值为key的节点
public void removeAllKey(int key);
//得到单链表的长度
public int size();
public void display();
public void clear();

注意:在实现这些方法的时候,要注意遍历链表 while 循环判断条件应该是 cur.next != null 还是 cur != null

插入删除元素的时候考虑边界条件及特殊情况(只有一个结点、空结点等)

头插

public void addFirst(int data) {Node newNode = new Node(data);if (this.head == null) {     // 当为空链表时,只需要让head指向该结点即可head = newNode;        return;}newNode.next = head;head = newNode;
}

尾插

注意当链表为空的时候特殊处理. 不为空时,通过遍历链表的方式找尾,然后再插入

public void addLast(int data) {Node newNode = new Node(data);if (head == null) {head = newNode;return;}Node cur = head;while (cur.next != null) {cur = cur.next;}cur.next = newNode;
}

得到单链表的长度

public int size() {int count = 0;Node cur = this.head;while (cur != null) {cur = cur.next;count++;}return count;
}

任意位置插入

设定第一个数据节点为0号下标,要注意考虑会有头插,尾插,下标不合法等情况

public void addIndex(int index, int data) {if (index < 0 || index > this.size()) {throw new RuntimeException("index" + ":" + index);}Node newNode = new Node(data);if (index == 0) {    // 头插this.addFirst(data);return;}if (index == this.size()) {   // 尾插this.addLast(data);return;}Node cur = this.head;while (index - 1 != 0) {cur = cur.next;index--;}newNode.next = cur.next;cur.next = newNode;
}

查找是否包含关键字key

遍历链表,将结点数据和key进行比较

public boolean contains(int key) {Node cur = this.head;while (cur != null) {if (cur.data == key) {return true;}cur = cur.next;}return false;
}

删除第一次出现关键字为key的节点

遍历链表,找到第一个为key的结点然后删除,要考虑链表为空,头删,等特殊情况

public void remove(int key) {if (this.head == null) {return;}if (head.data == key) {   // 头删head = head.next;return;}Node cur = this.head;while (cur.next != null) {if (cur.next.data == key) {break;}cur = cur.next;}if (cur.next != null) {cur.next = cur.next.next;}
}

打印单链表

public void display() {Node cur = this.head;while (cur != null) {System.out.print(cur.data + "->");cur = cur.next;}System.out.print("null");
}

清空链表

在用完链表之后使用clear() 方法防止发生内存泄漏(查看进程号jps, 重定向 jmap -histo:live 6666 > e:\niubi.txt)

public void clear() {this.head = null;
}

删除所有值为key的节点

注意:1. prev到底什么时候才往后跳, 第一个结点不应该刚开始就判断,而应该后面的都删完了之后再判断第一个结点

public void removeAll(int key) {if (this.head == null) {return;}Node prev = this.head;Node cur = this.head.next;while (cur != null) {if (cur.data == key) {prev.next = cur.next;cur = cur.next;} else {prev = cur;cur = cur.next;      // 不是key,cur和prev都往后跳}}if (this.head.data == key) {this.head = this.head.next;}
}

2. 双向链表

这里以双向无头非循环链表为例来介绍,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Bvzi6qI-1641391981418)(C:\Users\lycre\AppData\Roaming\Typora\typora-user-images\image-20220105182248704.png)]

属性定义

结点对象及构造方法和单线表相同

链表对象

链表对象多一个tail属性,指向链表的尾结点

public class MyLinkedList{public Node head;public Node tail;
}

构造方法

无参构造方法

public MyLinkedList() {this.head = null;this.tail = null;
}

链表的实现

//头插法
public void addFirst(int data);
//尾插法
public void addLast(int data);
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data);
//查找是否包含关键字key
public boolean contains(int key);
//删除第一次出现关键字为key的节点
public void remove(int key);
//删除所有值为key的节点
public void removeAllKey(int key);
//得到链表的长度
public int size();
//打印链表
public void display();
//清空链表,释放内存
public void clear();

头插

头插的过程中要注意空链表的情况

public void addFirst(int data) {Node newNode = new Node(data);if (this.head == null) {    // 链表为空时this.head = newNode;this.tail = newNode;} else {newNode.next = this.head;this.head.prev = newNode;this.head = newNode;}
}

尾插

public void addLast(int data) {Node newNode = new Node(data);if (this.head == null) {   // 链表为空的情况this.head = newNode;this.tail = newNode;} else {newNode.prev = tail;this.tail.next = newNode;this.tail = newNode;}
}

得到链表的长度

public int size() {int count = 0;Node cur = this.head;while (cur != null) {count++;cur = cur.next;}return count;
}

任意位置插入

在任意位置插入元素需要考虑的情况会比较多,我们应该先将可能的情况都罗列出来,再写,避免忽略一些情况

注意对index有效性进行判断

public void addIndex(int index,int data) {if (index < 0 || index > this.size()) {throw new RuntimeException("index : "  + index);}Node newNode = new Node(data);if (index == 0) {if (this.head == null) {   // 头插this.head = newNode;this.tail = newNode;} else {newNode.next = this.head;this.head.prev = newNode;this.head = newNode;}} else if (index == this.size()) {   // 尾插newNode.prev = this.tail;this.tail.next = newNode;this.tail = newNode;} else {Node cur = this.head;while (index != 0) {cur = cur.next;index--;}Node prevNode = cur.prev;newNode.next = cur;newNode.prev = prevNode;prevNode.next = newNode;cur.prev = newNode;}
}

查找是否包含关键字key

直接遍历链表,逐一对比

public boolean contains(int key) {Node cur = this.head;while (cur != null) {if (cur.data == key) {return true;}}return false;
}

删除第一次出现关键字为key的结点

涉及到头删,尾删,空链表等情况

public void remove(int key) {if (this.head == null) {return;}Node cur = this.head;while (cur != null) {        // 找第一个为key的结点if (cur.data == key) {break;}cur = cur.next;} if (cur == null) {return;          //没找到,直接返回}if (cur == this.head) {       // 头删this.head = head.next;this.head.prev = null;  return;}if (cur == this.tail) {     // 尾删cur.prev.next = null;this.tail = cur.prev;return;}Node prevNode = cur.prev;Node nextNode = cur.next;prevNode.next = nextNode;nextNode.prev = prevNode;
}

删除所有值为key的节点、

public void removeAllKey(int key) {if (this.head == null) {return;}Node prevNode = this.head;Node cur = this.head.next;while (cur != null) {if (cur.data == key) {prevNode.next = cur.next;cur = cur.next;if (cur == null) {this.tail = prevNode;     // 最后一个结点值为key} else {cur.prev = prevNode;}} else {cur = cur.next;prevNode = prevNode.next;}}if (this.head.data == key) {this.head = this.head.next;if (this.head == null) {this.tail = null;      // 注意当链表中所有元素均为key时,在最后不仅要判断head,也要改tailreturn;}this.head.prev = null;}
}

打印链表

public void display() {Node cur = this.head;while (cur != null) {System.out.print(cur.data + "->");cur = cur.next;}System.out.print("null");
}

清空链表,释放内存

public void clear() {if (this.head == null) {return;}Node cur = this.head;Node nextNode;while (cur != null) {nextNode = cur.next;cur.prev = null;cur.next = null;cur = nextNode;}this.tail = null;
}

注意清空双向链表和单向链表是有很大区别的,不能直接将head置为null,因为后面的结点还保存这前面结点的引用,所以我们应该要将所有结点的next域和prev域置空

欢迎大家关注!!!

一起学习交流 !!!

让我们将编程进行到底!!!

--------------整理不易,请三连支持------------------

【数据结构】手撕单链表相关推荐

  1. [ 数据结构 -- 手撕排序算法第三篇 ] 希尔排序

    手撕排序算法系列之:希尔排序. 从本篇文章开始,我会介绍并分析常见的几种排序,大致包括插入排序,冒泡排序,希尔排序,选择排序,堆排序,快速排序,归并排序等. 大家可以点击此链接阅读其他排序算法:排序算 ...

  2. 手写识别底层原理_LinkedList底层原理和手写单链表

    2.1 单链表技能点 · 认识单链表 o 特点 数据元素的存储对应的是不连续的存储空间,每个存储结点对应一个需要存储的数据元素. 每个结点是由数据域和指针域组成. 元素之间的逻辑关系通过存储节点之间的 ...

  3. 数据结构与算法--单链表相关面试题

    此文章仅作为自己学习过程中的记录和总结,同时会有意地去用英文来做笔记,一些术语的英译不太准确,内容如有错漏也请多指教,谢谢! 一.概述 获取单链表的有效元素个数[新浪面试题1] 获取单链表倒数第k个结 ...

  4. python数据结构基础(单链表,多链表,二叉树)

    python数据结构基础(单链表,多链表,二叉树) 数据结构指数据对象中数据元素之间的关系 Python 给我们提供了很多现成的数据结构类型,这些系统自己定义好的,不需要我们自己去定义的数据结构叫做 ...

  5. c语言用单链表实现lru算法,手写单链表实现和LRU算法模拟

    手写单链表,实现增删改查 package top.zcwfeng.java.arithmetic.lru; //单链表 public class LinkedList { Node list; int ...

  6. 数据结构之——《单链表》

    数据结构之--<单链表> 1.链表概念 2.链表分类 3.接口函数实现 1.链表概念 链表是一种物理存储结构上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现. ...

  7. 数据结构精讲——单链表

    新手必会数据结构精讲--单链表 链表的介绍 概念:链表是一种物理存储结构上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 . 实际中链表的结构非常多样,以下情况组合起来就 ...

  8. 应对嵌入式校招面试手撕之——链表

    笔者投的是嵌入式软件岗,发现手撕代码难度上比软件开发岗是要简单一些的,基本都是leetcode的medeium和easy的水平,基本难的算法不会考,集中在字符串处理和链表,现在把一些常见的链表题做一下 ...

  9. [数据结构与算法] 单链表的简单demo

    Vc6之下编译通过.. 1 /******************************************************* 2 * @: Project: 单链表数据结构演示 3 * ...

最新文章

  1. antd Select 使用动态数据渲染选择项
  2. Python第一次写的代码
  3. Android 广播机制以及用法详解 (转)
  4. 外卖和快递行业数据_抢人大战愈演愈烈,东莞再现用工荒!不只流向外卖、快递等行业...
  5. 作者:王建新(1969-),博士,中南大学信息科学与工程学院教授,中国计算机学会高级会员。...
  6. Android 原创新作 超级水平仪 发布
  7. 大学生创新创业训练计划讲解(大创)
  8. 华三基础命令(单臂,超级vlan,远程,聚合)
  9. OpenCV4学习笔记(27)——轮廓的最大内接圆
  10. 人工智能系列电子书分享
  11. Win10_此电脑_额外文件夹
  12. MPI并行编程: 矩阵乘法,积分计算,PSPR排序
  13. 百度地图API调用实现获取经纬度以及标注
  14. python函数编写脚本
  15. 牛客网入门题--最大公约数与最小公倍数
  16. 笔记本无线连接打印机
  17. 关于TC油封-什么是TC油封?
  18. 读行学区块链专栏 | 全球区块链资讯Top10
  19. java host头攻击漏洞_Java Web项目漏洞:检测到目标URL存在http host头攻击漏洞解决办法...
  20. mamp mysql启动失败_MAMP PRO mysql无法启动

热门文章

  1. 张小龙在2017微信公开课PRO版讲了什么(附演讲实录和2016微信数据报告)
  2. Win7系统DNS服务器未响应的解决办法【系统天地】
  3. 英雄联盟主播有哪些精彩操作值得看?
  4. 国密局发布16项密码行业标准 2022年5月1日起实施
  5. SqlServer geometry 转 geography
  6. 浙江台州“安乐死”案承办法官首次披露庭审细节
  7. idea中使用git和svn
  8. 第一次漏洞分析(暴雷漏洞CVE-2012-1889)
  9. android开发 dts、各种接口porting
  10. 以网游服务端的网络接入层设计为例,理解实时通信的技术挑战