一、定义
1.1 概念
在计算机科学中,链表是数据元素的线性集合,其每个元素都指向下一个元素,元素存储上并不连续
1.2 分类
- 单向链表:每个元素只知道其下一个元素是谁
- 双向链表:每个元素知道其上一个元素和下一个元素
- 循环链表:通常的链表尾节点 tail 指向的都是 null,而循环链表的 tail 指向的是头节点 head
链表内还有一种特殊的节点称为哨兵(Sentinel)节点,也叫做哑元( Dummy)节点,它不存储数据,通常用作头尾,用来简化边界判断。
随机访问性能
根据 index 查找,时间复杂度
插入或删除性能
- 起始位置:
- 结束位置:如果已知 tail 尾节点是 ,不知道 tail 尾节点是
- 中间位置:根据 index 查找时间 +
二、单向链表
2.1 定义
public class SinglyLinkedList {
// 头节点
private SingleNode head;
// 节点类
static class SingleNode {
private int value;
private SingleNode next;
public SingleNode(int value, SingleNode next) {
this.value = value;
this.next = next;
}
}
}
- SingleNode 定义为内部类,是为了对外隐藏实现细节,没必要让类的使用者关心 Node 结构
- 定义为 static 内部类,是因为 SingleNode 不需要与 SinglyLinkedList 实例相关,多个 SinglyLinkedList实例能共用 SingleNode 类定义
2.2 头部添加
private static void addFirst(int value) {
//链表为空
// head = new SingleNode(value, null);
//链表非空
head = new SingleNode(value, head);
}
2.3 遍历
public class SinglyLinkedList implements Iterable<Integer> {
// .....
/**
* 遍历while
*/
private static void loopWhile(Consumer<Integer> consumer) {
SingleNode pointer = head;
while (pointer != null) {
consumer.accept(pointer.value);
pointer = pointer.next;
}
}
/**
* 遍历for
*
* @param consumer
*/
private static void loopFor(Consumer<Integer> consumer) {
for (SingleNode pointer = head; pointer != null; pointer = pointer.next) {
consumer.accept(pointer.value);
}
}
/**
* 递归遍历
*/
private static void loopRecursion(Consumer<Integer> before, Consumer<Integer> after) {
recursion(head, before, after);
}
private static void loop(SingleNode node) {
if (node == null) {
return;
}
System.out.println("before:" + node.value);
loop(node.next);
System.out.println("after:" + node.value);
}
/**
* 递归调用
* @param node
* @param before
* @param after
*/
private static void recursion(SingleNode node, Consumer<Integer> before, Consumer<Integer> after) {
if (node == null) {
return;
}
before.accept(node.value);
recursion(node.next, before, after);
after.accept(node.value);
}
// 迭代器遍历
private static class IntegerIterator implements Iterator<Integer> {
// 指针
SingleNode pointer = head;
/**
* 是否有下一个元素
*
* @return
*/
@Override
public boolean hasNext() {
return pointer != null;
}
/**
* 返回当前元素,并指向下一个元素
*
* @return
*/
@Override
public Integer next() {
int value = pointer.value;
pointer = pointer.next;
return value;
}
}
以上遍历都可以把要做的事以 Consumer 函数的方式传递进来
- Consumer 的规则是一个参数,无返回值,因此像 System.out::println 方法等都是 Consumer
- 调用 Consumer 时,将当前节点 curr.value 作为参数传递给它
迭代器遍历:
- hasNext 用来判断是否还有必要调用 next
- next 做两件事
- 返回当前节点的 value
- 指向下一个节点
2.4 尾部添加
/**
* 尾插
*
* @param value
*/
private static void addLast(int value) {
SingleNode last = findLast();
if (last == null) {
addFirst(value);
} else {
last.next = new SingleNode(value, null);
}
}
/**
* 找到最后一个元素
*
* @return
*/
private static SingleNode findLast() {
if (head == null) {
return null;
}
SingleNode pointer = head;
while (pointer.next != null) {
pointer = pointer.next;
}
return pointer;
}
2.5 根据索引获取元素
/**
* 根据索引获取节点值
*
* @param index
* @return
*/
public static int getNodeVal(int index) {
SingleNode node = findNode(index);
if (node == null) {
throw new IllegalArgumentException(String.format("index [%d] 不合法 %n", index));
}
return node.value;
}
/**
* 根据索引查找节点
*
* @param index
* @return
*/
private static SingleNode findNode(int index) {
int i = 0;
for (SingleNode pointer = head; pointer != null; pointer = pointer.next, i++) {
if (i == index) {
return pointer;
}
}
return null;
}
2.6 插入元素(任意位置)
/**
* 往对应位置index插入元素
*
* @param index
* @param val
*/
private static void addByIndex(int index, int val) {
if (index == 0) {
addFirst(val);
} else {
//获取当前要插入的索引的上一个元素
SingleNode pre = findNode(index - 1);
if (pre == null) {
throw new IllegalArgumentException(String.format("index [%d] 不合法 %n", index));
}
pre.next = new SingleNode(val, pre.next);
}
}
2.7 删除元素
/**
* 删除第一个节点
*/
public static void removeFirst() {
if (head == null) {
throw new IllegalArgumentException("没有可删除的节点");
}
head = head.next;
}
/**
* 按照索引删除对应的节点
* @param index
*/
public static void remove(int index) {
if (index == 0) {
removeFirst();
} else {
SingleNode pre = findNode(index - 1);
if (pre == null || pre.next == null) {
throw new IllegalArgumentException("没有可删除的节点");
}
pre.next = pre.next.next;
}
}
2.8 带哨兵的单向链表操作
public class SentinelSingleLinkedList implements Iterable<Integer> {
/**
* 让头指针指向哨兵节点 减少非空判断
*/
private static SingleNode head = new SingleNode(-1,null);
@Override
public Iterator<Integer> iterator() {
return new IntegerIterator();
}
static class SingleNode {
private int value;
private SingleNode next;
public SingleNode(int value, SingleNode next) {
this.value = value;
this.next = next;
}
}
/**
* 头插
*
* @param value
*/
private static void addFirst(int value) {
//链表为空
// head = new SingleNode(value, null);
//链表非空
// head = new SingleNode(value, head);
addByIndex(0, value);
}
/**
* 尾插
*
* @param value
*/
private static void addLast(int value) {
SingleNode last = findLast();
// if (last == null) {
// addFirst(value);
// } else {
last.next = new SingleNode(value, null);
// }
}
/**
* 根据索引查找节点
*
* @param index
* @return
*/
private static SingleNode findNode(int index) {
int i = -1;
for (SingleNode pointer = head; pointer != null; pointer = pointer.next, i++) {
if (i == index) {
return pointer;
}
}
return null;
}
/**
* 往对应位置index插入元素
*
* @param index
* @param val
*/
private static void addByIndex(int index, int val) {
// if (index == 0) {
// addFirst(val);
// } else {
//获取当前要插入的索引的上一个元素
SingleNode pre = findNode(index - 1);
if (pre == null) {
throw new IllegalArgumentException(String.format("index [%d] 不合法 %n", index));
}
pre.next = new SingleNode(val, pre.next);
// }
}
/**
* 根据索引获取节点值
*
* @param index
* @return
*/
public static int getNodeVal(int index) {
SingleNode node = findNode(index);
if (node == null) {
throw new IllegalArgumentException(String.format("index [%d] 不合法 %n", index));
}
return node.value;
}
/**
* 删除第一个节点
*/
public static void removeFirst() {
// if (head == null) {
// throw new IllegalArgumentException("没有可删除的节点");
// }
// head = head.next;
remove(0);
}
/**
* 按照索引删除对应的节点
* @param index
*/
public static void remove(int index) {
// if (index == 0) {
// removeFirst();
// } else {
SingleNode pre = findNode(index - 1);
if (pre == null || pre.next == null) {
throw new IllegalArgumentException("没有可删除的节点");
}
pre.next = pre.next.next;
// }
}
/**
* 遍历while
*/
private static void loopWhile(Consumer<Integer> consumer) {
SingleNode pointer = head.next;
while (pointer != null) {
consumer.accept(pointer.value);
pointer = pointer.next;
}
}
/**
* 遍历for
*
* @param consumer
*/
private static void loopFor(Consumer<Integer> consumer) {
for (SingleNode pointer = head.next; pointer != null; pointer = pointer.next) {
consumer.accept(pointer.value);
}
}
/**
* 找到最后一个元素
*
* @return
*/
private static SingleNode findLast() {
// 因为头指针指向的是哨兵 所以不会为空
// if (head == null) {
// return null;
// }
SingleNode pointer = head;
while (pointer.next != null) {
pointer = pointer.next;
}
return pointer;
}
}
三、双向链表
带哨兵的双向链表
public class SentinelDoubleLinkedList implements Iterable<Integer> {
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
DoubleNode pointer = head.next;
@Override
public boolean hasNext() {
return pointer != tail;
}
@Override
public Integer next() {
Integer value = pointer.value;
pointer = pointer.next;
return value;
}
};
}
static class DoubleNode {
DoubleNode pre;
int value;
DoubleNode next;
public DoubleNode(DoubleNode pre, int value, DoubleNode next) {
this.pre = pre;
this.value = value;
this.next = next;
}
}
/**
* 头哨兵
*/
private static DoubleNode head;
/**
* 尾哨兵
*/
private static DoubleNode tail;
public SentinelDoubleLinkedList() {
head = new DoubleNode(null, -1, null);
tail = new DoubleNode(null, -2, null);
head.next = tail;
tail.pre = head;
}
/**
* 根据索引查找节点
*
* @param index
* @return
*/
private static DoubleNode findNodeByIndex(int index) {
int i = -1;
for (DoubleNode pointer = head; pointer != tail; i++, pointer = pointer.next) {
if (i == index) {
return pointer;
}
}
return null;
}
/**
* 根据索引位置插入值
*
* @param index
* @param value
*/
private static void addByIndex(int index, int value) {
//找到要插入的上一个节点
DoubleNode pre = findNodeByIndex(index - 1);
if (pre == null) {
throw new IllegalArgumentException();
}
DoubleNode next = pre.next;
//要插入的新节点
DoubleNode node = new DoubleNode(pre, value, next);
pre.next = node;
next.pre = node;
}
/**
* 从头部添加节点
*
* @param value
*/
private static void addFirst(int value) {
addByIndex(0, value);
}
/**
* 从尾部添加节点
*
* @param value
*/
private static void addLast(int value) {
// 最后一个节点
DoubleNode last = tail.pre;
DoubleNode node = new DoubleNode(last, value, tail);
last.next = node;
tail.pre = node;
}
/**
* 删除第一个节点
*/
public void removeFirst() {
removeByIndex(0);
}
/**
* 删除最后一个节点
*/
public void removeLast() {
if (tail.pre == head) {
throw new IllegalArgumentException();
}
// 最后一个节点的前一个节点
DoubleNode node = tail.pre.pre;
node.next = tail;
tail.pre = node;
}
/**
* 按索引删除节点
*
* @param index
*/
public void removeByIndex(int index) {
// 要删除的上一个
DoubleNode pre = findNodeByIndex(index - 1);
// 找不到pre或者要删除的是尾哨兵的时候
if (pre == null || pre.next == tail) {
throw new IllegalArgumentException();
}
// 要删除的下一个
DoubleNode next = pre.next.next;
pre.next = next;
next.pre = pre;
}
}
四、双向环形链表
哨兵既作为头,也作为尾
带哨兵的双向环形链表
public class SentinelDoubleCircularLinkedList implements Iterable<Integer> {
// 哨兵节点
private static DoubleCircularNode sentinel = new DoubleCircularNode(null, -1, null);
public SentinelDoubleCircularLinkedList() {
sentinel.pre = sentinel;
sentinel.next = sentinel;
}
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
DoubleCircularNode pointer = sentinel.next;
@Override
public boolean hasNext() {
return pointer != sentinel;
}
@Override
public Integer next() {
Integer value = pointer.value;
pointer = pointer.next;
return value;
}
};
}
private static class DoubleCircularNode {
DoubleCircularNode pre;
int value;
DoubleCircularNode next;
public DoubleCircularNode(DoubleCircularNode pre, int value, DoubleCircularNode next) {
this.pre = pre;
this.value = value;
this.next = next;
}
}
/**
* 从头部添加节点
*/
private static void addFirst(int value) {
//前驱
DoubleCircularNode pre = sentinel;
// 后继
DoubleCircularNode next = sentinel.next;
DoubleCircularNode node = new DoubleCircularNode(pre, value, next);
pre.next = node;
next.pre = node;
}
/**
* 从尾部添加节点
*
* @param value
*/
private static void addLast(int value) {
DoubleCircularNode node = new DoubleCircularNode(sentinel.pre, value, sentinel);
sentinel.pre.next = node;
sentinel.pre = node;
}
/**
* 从头部删除节点
*/
private static void removeFirst() {
//要删除的元素
DoubleCircularNode remove = sentinel.next;
if (remove == sentinel) {
throw new IllegalArgumentException();
}
sentinel.next = remove.next;
remove.next.pre = sentinel;
}
/**
* 从尾部删除节点
*/
private static void removeLast() {
//要删除的节点
DoubleCircularNode remove = sentinel.pre;
if (remove == sentinel) {
throw new IllegalArgumentException();
}
remove.pre.next = sentinel;
sentinel.pre = remove.pre;
}
/**
* 根据值删除对应的节点
*/
private static void removeByValue(int value) {
//要删除的节点
DoubleCircularNode remove = findByValue(value);
if (remove == null) {
return;
}
remove.pre.next = remove.next;
remove.next.pre = remove.pre;
}
/**
* 根据值查找节点
*
* @return
*/
private static DoubleCircularNode findByValue(int value) {
DoubleCircularNode pointer = sentinel.next;
while (pointer != sentinel) {
if (pointer.value == value) {
return pointer;
}
pointer = pointer.next;
}
return null;
}
}