目录
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
在我看来,链表和火车车厢的结构比较类似,是由一节一节的车厢连接起来的。

注意:
实际中链表的结构主要分以下几个方面:



注意:Java集合中LinkedList底层是无头双向循环链表

注意:
单链表中头节点很烦人,不论是插入还是删除均需考虑头节点,因为其没有前驱。
所有的点操作(".")均需注意空指针情况
- //头插法
- public void addFirst(int data) {
- //1.拿到一个实体
- ListNode node = new ListNode(data);
-
- //2.插入
- //如果是第一次插入,直接找到头节点
- if (this.head == null) {
- this.head = node;
- last = node;
- }else {
- //不是第一次插入
- node.next = head;
- head.prev = node;
- head = node;
- }
- }
- //尾插法
- public void addLast(int data) {
- ListNode node = new ListNode(data);
- if (head == null) {
- head = node;
- last = node;
- }else {
- ListNode cur = head;
- while (cur.next != null) {
- cur = cur.next;
- }
- cur.next = node;
- }
- }
有了头插法和尾插法我们就能调用这两个方法来创建链表,这里我就没吧代码拿出来了。
- //检查index下标是否合法
- private void checkIndex(int index) {
- if(index < 0 || index > size()) {
- //throw new IndexOutOfBoundsException("index位置不合法!");
-
- throw new IndexNotLegalException("index位置不合法!");
- }
- }
-
- //任意位置插入,第一个数据节点为0号下标
- public void addIndex(int index,int data) {
- checkIndex(index);
- if (index == 0) {
- addFirst(data);
- return;
- }
- if (index == size()) {
- addLast(data);
- return;
- }
-
- //不是头插尾插,且index合法
- ListNode node = new ListNode(data);
- ListNode cur = findIndexSubOfOne(index);
- node.next = cur.next;
- cur.next = node;
- }
-
- private ListNode findIndexSubOfOne(int index) {
- ListNode cur = head;
- while (index - 1 != 0) {
- cur = cur.next;
- index--;
- }
- return cur;
- }
就是简单的遍历链表
- //查找是否包含关键字key是否在单链表当中
- public boolean contains(int key) {
- ListNode cur = head;
- while (cur != null) {
- if(cur.val == key) {
- return true;
- }
- cur = cur.next;
- }
- return false;
- }
在删除某个节点的时候,我们以防在删除之后,这个节点后面的节点丢失,我们在删除这个节点的时候要先把这个节点后面的节点记录并链接起来
- //删除第一次出现关键字为key的节点
- public void remove(int key) {
- //情况没处理
- if(head.val == key) {
- head = head.next;
- return;
- }
- ListNode cur = searchPrevOfKey(key);
- if(cur == null)
- return;
- ListNode del = cur.next;
- cur.next = del.next;
- }
-
- /**
- * 找到关键字key的前驱
- * @param key
- * @return
- */
- private ListNode searchPrevOfKey(int key) {
- ListNode cur = this.head;
- while (cur.next != null) {
- if(cur.next.val == key) {
- return cur;
- }
- cur = cur.next;
- }
- return null;
- }
- //删除所有值为key的节点
- public void removeAllKey(int key) {
- if(head == null) return;
- ListNode prev = head;
- ListNode cur = head.next;
- while (cur != null) {
- if(cur.val == key) {
- prev.next = cur.next;
- cur = cur.next;
- }else {
- prev = cur;
- cur = cur.next;
- }
- }
- if(head.val == key) {
- head = head.next;
- }
- }
- //得到单链表的长度
- public int size() {
- ListNode cur = head;
- int count = 0;
- while (cur != null) {
- count++;
- cur = cur.next;
- }
- return count;
- }
- //打印
- public void display() {
- ListNode cur = head;
- while (cur != null) {
- System.out.print(cur.val+" ");
- cur = cur.next;
- }
- System.out.println();
- }
- //清空
- public void clear() {
- ListNode cur = head;
- while (cur != null) {
- ListNode curNext = cur.next;
- cur.next = null;
- cur = curNext;
- }
- head = null;
- }
首先定义这个类哦

- //头插法
- public void addFirst(int data){
- ListNode node = new ListNode(data);
-
- if (head == null) {
- head = node;
- last = node;
- }else {
- node.next = head;
- head.pre = node;
- head = node;
- }
- }
- //尾插法
- public void addLast(int data) {
- ListNode node = new ListNode(data);
-
- if (head == null) {
- head = node;
- last = node;
- }else {
- last.next = node;
- node.pre = last;
- last = last.next;
- }
- }
- //计算链表长度
- public int size() {
- int count = 0;
- ListNode cur = head;
- while (cur != null) {
- count++;
- cur = cur.next;
- }
-
- return count;
- }
- //检查index是否合法
- private boolean checkIndex(int index) {
- if (index < 0 || index > size()) {
- throw new IndexOutOfBoundsException("插入位置不合法!");
- }else {
- return true;
- }
- }
-
- //任意位置插入,第一个数据节点为0号下标
- public void addIndex(int index,int data) {
- if(checkIndex(index) == true) {
- if (index == 0) {
- addFirst(index);
- }
- if (index == size()) {
- addLast(index);
- }
-
- ListNode node = new ListNode(data);
- //找到当前index节点的地址
- ListNode cur = findIndex(index);
-
- node.next = cur;
- cur.pre.next = node;
- node.pre = cur.pre;
- cur.pre = node;
-
- }
-
- }
-
- //找到当前index节点的地址
- private ListNode findIndex(int index) {
- ListNode cur = head;
- while (index != 0) {
- cur = cur.next;
- index--;
- }
- return cur;
- }
- //查找是否包含关键字key是否在单链表当中
- public boolean contains(int key) {
- ListNode cur = head;
- while (cur != null) {
- if(cur.val == key) {
- return true;
- }
- cur = cur.next;
- }
- return true;
- }
- //删除第一次出现关键字为key的节点
- public void remove(int key) {
- ListNode cur = head;
- while (cur != null) {
- if(cur.val == key) {
- //处理头部问题
- if(cur == head) {
- head = head.next;
- if(head != null) {
- //防止只有一个节点
- head.pre = null;
- }
- }else {
- cur.pre.next = cur.next;
- if(cur.next != null) {
- //删除的不是尾巴节点
- cur.next.pre = cur.pre;
- }else {
- //删除的是尾巴节点
- last = cur.pre;
- }
- }
- return;
- }
- cur = cur.next;
- }
-
- /*while (cur != null) {
- if (cur.val == key) {
- cur.pre.next = cur.next;
- cur.pre = cur.next.pre;
- }else {
- cur = cur.next;
- }
- }*/
- }
- //删除所有值为key的节点
- public void removeAllKey(int key) {
- ListNode cur = head;
- while (cur != null) {
- if(cur.val == key) {
- //处理头部问题
- if(cur == head) {
- head = head.next;
- if(head != null) {
- //防止只有一个节点
- head.pre = null;
- }
- }else {
- cur.pre.next = cur.next;
- if(cur.next != null) {
- //删除的不是尾巴节点
- cur.next.pre = cur.pre;
- }else {
- //删除的是尾巴节点
- last = cur.pre;
- }
- }
-
- }
- cur = cur.next;
- }
- }
- //打印链表
- public void display() {
- ListNode cur = head;
- while (cur != null) {
- System.out.print(cur.val + " ");
- cur = cur.next;
- }
- System.out.println();
- }
- //清空链表
- public void clear() {
- this.head = null;
- }
LinkedList的底层是双向链表结构,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后是通过引用将节点连接起来的,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。
下面是LinkedList的部分源码

在集合框架中,LinkedList也实现了List接口

| 方法 | 解释 |
| LinkedList() | 无参构造 |
| public LinkedList(Collection extends E> c) | 使用其他集合容器中元素构造List |
| 方法 | 解释 |
| boolean add(E e) | 尾插e |
| void add(int index, E element) | 将 e 插入到 index 位置 |
| boolean addAll(Collection extends E> c) | 尾插 c 中的元素 |
| E remove(int index) | 删除 index 位置元素 |
| boolean remove(Object o) | 删除遇到的第一个 o |
| E get(int index) | 获取下标 index 位置元素 |
| E set(int index, E element) | 将下标 index 位置元素设置为 element |
| void clear() | 清空 |
| boolean contains(Object o) | 判断 o 是否在线性表中 |
| int indexOf(Object o) | 返回第一个 o 所在下标 |
| int lastIndexOf(Object o) | 返回最后一个 o 的下标 |
| List | 截取部分 list |
1、下图是add方法的源码,可以看到源码采用的是尾插

| 不同点 | ArrayList | LinkedList |
| 存储空间上 | 物理上一定连续 | 逻辑上连续,物理上不一定连续 |
| 随机访问 | 支持 O(1) | 不支持 O(n) |
| 头插 | 需要搬移元素,效率低O(n) | 只需修改引用的指向,时间复杂幅度O(1) |
| 插入 | 空间不够时需要扩容 | 没用容量的概念 |
| 应用 | 需要频繁的使用时+元素高效存储 | 任意位置的插入和删除频繁 |