一、定義
1.1 概念
在計(jì)算機(jī)科學(xué)中,鏈表是數(shù)據(jù)元素的線性集合,其每個(gè)元素都指向下一個(gè)元素,元素存儲(chǔ)上并不連續(xù)
1.2 分類
- 單向鏈表:每個(gè)元素只知道其下一個(gè)元素是誰(shuí)
- 雙向鏈表:每個(gè)元素知道其上一個(gè)元素和下一個(gè)元素
- 循環(huán)鏈表:通常的鏈表尾節(jié)點(diǎn) tail 指向的都是 null,而循環(huán)鏈表的 tail 指向的是頭節(jié)點(diǎn) head
image.png
鏈表內(nèi)還有一種特殊的節(jié)點(diǎn)稱為哨兵(Sentinel)節(jié)點(diǎn),也叫做啞元( Dummy)節(jié)點(diǎn),它不存儲(chǔ)數(shù)據(jù),通常用作頭尾,用來(lái)簡(jiǎn)化邊界判斷。
隨機(jī)訪問(wèn)性能
根據(jù) index 查找,時(shí)間復(fù)雜度
插入或刪除性能
- 起始位置:
- 結(jié)束位置:如果已知 tail 尾節(jié)點(diǎn)是
,不知道 tail 尾節(jié)點(diǎn)是
- 中間位置:根據(jù) index 查找時(shí)間 +
二、單向鏈表
2.1 定義
public class SinglyLinkedList {
// 頭節(jié)點(diǎn)
private SingleNode head;
// 節(jié)點(diǎn)類
static class SingleNode {
private int value;
private SingleNode next;
public SingleNode(int value, SingleNode next) {
this.value = value;
this.next = next;
}
}
}
- SingleNode 定義為內(nèi)部類,是為了對(duì)外隱藏實(shí)現(xiàn)細(xì)節(jié),沒(méi)必要讓類的使用者關(guān)心 Node 結(jié)構(gòu)
- 定義為 static 內(nèi)部類,是因?yàn)?SingleNode 不需要與 SinglyLinkedList 實(shí)例相關(guān),多個(gè) SinglyLinkedList實(shí)例能共用 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);
}
/**
* 遞歸調(diào)用
* @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;
/**
* 是否有下一個(gè)元素
*
* @return
*/
@Override
public boolean hasNext() {
return pointer != null;
}
/**
* 返回當(dāng)前元素,并指向下一個(gè)元素
*
* @return
*/
@Override
public Integer next() {
int value = pointer.value;
pointer = pointer.next;
return value;
}
}
以上遍歷都可以把要做的事以 Consumer 函數(shù)的方式傳遞進(jìn)來(lái)
- Consumer 的規(guī)則是一個(gè)參數(shù),無(wú)返回值,因此像 System.out::println 方法等都是 Consumer
- 調(diào)用 Consumer 時(shí),將當(dāng)前節(jié)點(diǎn) curr.value 作為參數(shù)傳遞給它
迭代器遍歷:
- hasNext 用來(lái)判斷是否還有必要調(diào)用 next
- next 做兩件事
- 返回當(dāng)前節(jié)點(diǎn)的 value
- 指向下一個(gè)節(jié)點(diǎn)
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);
}
}
/**
* 找到最后一個(gè)元素
*
* @return
*/
private static SingleNode findLast() {
if (head == null) {
return null;
}
SingleNode pointer = head;
while (pointer.next != null) {
pointer = pointer.next;
}
return pointer;
}
2.5 根據(jù)索引獲取元素
/**
* 根據(jù)索引獲取節(jié)點(diǎn)值
*
* @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;
}
/**
* 根據(jù)索引查找節(jié)點(diǎn)
*
* @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 插入元素(任意位置)
/**
* 往對(duì)應(yīng)位置index插入元素
*
* @param index
* @param val
*/
private static void addByIndex(int index, int val) {
if (index == 0) {
addFirst(val);
} else {
//獲取當(dāng)前要插入的索引的上一個(gè)元素
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 刪除元素
/**
* 刪除第一個(gè)節(jié)點(diǎn)
*/
public static void removeFirst() {
if (head == null) {
throw new IllegalArgumentException("沒(méi)有可刪除的節(jié)點(diǎn)");
}
head = head.next;
}
/**
* 按照索引刪除對(duì)應(yīng)的節(jié)點(diǎn)
* @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("沒(méi)有可刪除的節(jié)點(diǎn)");
}
pre.next = pre.next.next;
}
}
2.8 帶哨兵的單向鏈表操作
public class SentinelSingleLinkedList implements Iterable<Integer> {
/**
* 讓頭指針指向哨兵節(jié)點(diǎn) 減少非空判斷
*/
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);
// }
}
/**
* 根據(jù)索引查找節(jié)點(diǎn)
*
* @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;
}
/**
* 往對(duì)應(yīng)位置index插入元素
*
* @param index
* @param val
*/
private static void addByIndex(int index, int val) {
// if (index == 0) {
// addFirst(val);
// } else {
//獲取當(dāng)前要插入的索引的上一個(gè)元素
SingleNode pre = findNode(index - 1);
if (pre == null) {
throw new IllegalArgumentException(String.format("index [%d] 不合法 %n", index));
}
pre.next = new SingleNode(val, pre.next);
// }
}
/**
* 根據(jù)索引獲取節(jié)點(diǎn)值
*
* @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;
}
/**
* 刪除第一個(gè)節(jié)點(diǎn)
*/
public static void removeFirst() {
// if (head == null) {
// throw new IllegalArgumentException("沒(méi)有可刪除的節(jié)點(diǎn)");
// }
// head = head.next;
remove(0);
}
/**
* 按照索引刪除對(duì)應(yīng)的節(jié)點(diǎn)
* @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("沒(méi)有可刪除的節(jié)點(diǎn)");
}
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);
}
}
/**
* 找到最后一個(gè)元素
*
* @return
*/
private static SingleNode findLast() {
// 因?yàn)轭^指針指向的是哨兵 所以不會(huì)為空
// 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;
}
/**
* 根據(jù)索引查找節(jié)點(diǎn)
*
* @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;
}
/**
* 根據(jù)索引位置插入值
*
* @param index
* @param value
*/
private static void addByIndex(int index, int value) {
//找到要插入的上一個(gè)節(jié)點(diǎn)
DoubleNode pre = findNodeByIndex(index - 1);
if (pre == null) {
throw new IllegalArgumentException();
}
DoubleNode next = pre.next;
//要插入的新節(jié)點(diǎn)
DoubleNode node = new DoubleNode(pre, value, next);
pre.next = node;
next.pre = node;
}
/**
* 從頭部添加節(jié)點(diǎn)
*
* @param value
*/
private static void addFirst(int value) {
addByIndex(0, value);
}
/**
* 從尾部添加節(jié)點(diǎn)
*
* @param value
*/
private static void addLast(int value) {
// 最后一個(gè)節(jié)點(diǎn)
DoubleNode last = tail.pre;
DoubleNode node = new DoubleNode(last, value, tail);
last.next = node;
tail.pre = node;
}
/**
* 刪除第一個(gè)節(jié)點(diǎn)
*/
public void removeFirst() {
removeByIndex(0);
}
/**
* 刪除最后一個(gè)節(jié)點(diǎn)
*/
public void removeLast() {
if (tail.pre == head) {
throw new IllegalArgumentException();
}
// 最后一個(gè)節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)
DoubleNode node = tail.pre.pre;
node.next = tail;
tail.pre = node;
}
/**
* 按索引刪除節(jié)點(diǎn)
*
* @param index
*/
public void removeByIndex(int index) {
// 要?jiǎng)h除的上一個(gè)
DoubleNode pre = findNodeByIndex(index - 1);
// 找不到pre或者要?jiǎng)h除的是尾哨兵的時(shí)候
if (pre == null || pre.next == tail) {
throw new IllegalArgumentException();
}
// 要?jiǎng)h除的下一個(gè)
DoubleNode next = pre.next.next;
pre.next = next;
next.pre = pre;
}
}
四、雙向環(huán)形鏈表
哨兵既作為頭,也作為尾
image.png
帶哨兵的雙向環(huán)形鏈表
public class SentinelDoubleCircularLinkedList implements Iterable<Integer> {
// 哨兵節(jié)點(diǎn)
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;
}
}
/**
* 從頭部添加節(jié)點(diǎn)
*/
private static void addFirst(int value) {
//前驅(qū)
DoubleCircularNode pre = sentinel;
// 后繼
DoubleCircularNode next = sentinel.next;
DoubleCircularNode node = new DoubleCircularNode(pre, value, next);
pre.next = node;
next.pre = node;
}
/**
* 從尾部添加節(jié)點(diǎn)
*
* @param value
*/
private static void addLast(int value) {
DoubleCircularNode node = new DoubleCircularNode(sentinel.pre, value, sentinel);
sentinel.pre.next = node;
sentinel.pre = node;
}
/**
* 從頭部刪除節(jié)點(diǎn)
*/
private static void removeFirst() {
//要?jiǎng)h除的元素
DoubleCircularNode remove = sentinel.next;
if (remove == sentinel) {
throw new IllegalArgumentException();
}
sentinel.next = remove.next;
remove.next.pre = sentinel;
}
/**
* 從尾部刪除節(jié)點(diǎn)
*/
private static void removeLast() {
//要?jiǎng)h除的節(jié)點(diǎn)
DoubleCircularNode remove = sentinel.pre;
if (remove == sentinel) {
throw new IllegalArgumentException();
}
remove.pre.next = sentinel;
sentinel.pre = remove.pre;
}
/**
* 根據(jù)值刪除對(duì)應(yīng)的節(jié)點(diǎn)
*/
private static void removeByValue(int value) {
//要?jiǎng)h除的節(jié)點(diǎn)
DoubleCircularNode remove = findByValue(value);
if (remove == null) {
return;
}
remove.pre.next = remove.next;
remove.next.pre = remove.pre;
}
/**
* 根據(jù)值查找節(jié)點(diǎn)
*
* @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;
}
}