LinkedList 链表总结
前言
List 的两个重要实现类 ArrayList 和 LinkedList,各自的特点都是非常明显的。
ArrayList:
- 底层是数组,在逻辑上和物理上来说都是连续的,也就是说数组在内存上是存储在连续的空间里面的,这种存储的优势是通过 index(索引)可以实现快速查找和随机访问,操作复杂度只是O(1)。
- ArrayList 的缺点也很显然。对于插入和删除操作,每次插入或者删除元素,都需要对插入位置后面的元素进行后移或前移操作;如果数组已满还需要进行空间申请、拷贝数据、释放空间的一系列扩容操作,最差时间复杂度达到了O(N),对于规模大的数组来说不太友好。
LinkedList:
- 相比于ArrayList来说有着很大的不同。它将每个元素定义为一个节点,在节点中又申请了一块空间用于存放下一个节点的指针(pointer)。由于有指针的存在,链表在内存中一般来说都不是连续的,充分利用了内存中的碎片空间。
- 链表的插入、删除操作时间复杂度由于指针的存在变成了O(1)。但是,链表是不支持随机访问的,每次查找或者访问都需要遍历链表,从最差角度和平均角度来看,时间复杂度是O(N)。
LinkedList 和 ArrayList 作为基本的数据结构,在程序设计中经常用到,根据场景的不同使用不同的数据结构也是能够有很好的效果。相比较而言链表更抽象,更难理解,所以对链表 LinkedList 的实现以及 leetcode 刷过的所有链表题型进行一个归纳总结。
一、LinkedList(链表)
链表总体来说有很多类型,这里只是记录一下常用的单向链表、双向链表以及循环链表。
1. 单向链表
单向链表比较简单,每个节点只有两个域:数据域(val)和指针域(next),指针域 next 指向下一个节点,尾结点的 next 是 null 。
1.1 单向链表节点定义
/**
* Definition for singly-linked list.
*/
class SinglyNode {
int val;// 数据域
SinglyNode next;// 指针域
public SinglyNode(int val) {
this.val = val;
}
}
1.2 单向链表的实现
public class MySinglyLinkedList {
/**
* Definition for singly-linked list.
*/
class SinglyNode {
int val;
SinglyNode next;
public SinglyNode(int val) {
this.val = val;
}
}
public SinglyNode head;// 头结点
/**
* 头插法
*/
public void addFirst(int val) {
SinglyNode node = new SinglyNode(val);
// 第一次插入结点
if (this.head == null) {
this.head = node;
return;
}
node.next = this.head;
this.head = node;
}
/**
* 尾插法
*/
public void addLast(int val) {
SinglyNode node = new SinglyNode(val);
// 第一次插入结点
if (this.head == null) {
this.head = node;
return;
}
// 遍历到尾部
SinglyNode cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
// 插入结点
cur.next = node;
}
/**
* 任意位置插入
*/
public void addIndex(int index, int val) {
// index 不合法
if (index < 0 || index > size()) return;
// 下标为 0, 头插法
if (index == 0) {
addFirst(val);
return;
}
// 查找 index 的前驱
SinglyNode prev = searchPrev(index);
SinglyNode node = new SinglyNode(val);
// 插入
node.next = prev.next;
prev.next = node;
}
/**
* 查找 index 结点的前驱
*/
public SinglyNode searchPrev(int index) {
int count = 0;
SinglyNode cur = this.head;
while (count < index-1) {
cur = cur.next;
count++;
}
return cur;
}
/**
* 查找在单链表当中是否包含关键字 key
*/
public boolean contains(int val) {
SinglyNode cur = this.head;
while (cur != null) {
if (cur.val == val) {
return true;
}
cur = cur.next;
}
return false;
}
/**
* 删除第一次出现关键字为key的节点
*/
public void remove(int val) {
if (this.head.val == val) {
this.head = this.head.next;
return;
}
SinglyNode cur = this.head;// 遍历查找关键字
SinglyNode prev = null;// 遍历结点的前驱结点
// 查找关键字
while (cur != null) {
if (cur.val == val) {
prev.next