链表
定义
链表是数据元素的线性集合,其每个元素都指向下一个元素,元素存储上是不连续的。
分类
- 单向链表,每个元素只知道自己的下一个元素是谁。
- 双向链表,每个元素知道自己的上一个元素和下一个元素。
- 循环链表,通常链表尾部节点 tail 指向的为null,而循环链表的tail指向链表的头部节点 head。
链表中还有一种特殊的节点称之为,哨兵(Sentinel)节点,也称之为 哑元(Dummy)节点, 不存储数据,一般作为头尾,简化边界判断,如下如所示。
性能
随机访问
根据 i n d e x index index 查询,时间复杂度为 O ( n ) O(n) O(n) 。
插入或删除
- 起始位置,时间复杂度为, O ( 1 ) O(1) O(1)
- 结束位置,若已知尾部节点 tail ,时间复杂度为, O ( 1 ) O(1) O(1) ;否则为 O ( n ) O(n) O(n)
- 中间位置,根据 index 查找的时间复杂度 + O ( 1 ) O(1) O(1)
单项链表实现
方法实现
节点类、头部添加、循环遍历、尾部添加
// 单项链表
public class SinglyLinkedList implements Iterable<Integer>() {
private Node head = null; // 头指针
// 节点类
private static class Node() {
int value; // 值
Node node; // 下一个节点
public Node(int value, Node node) {
this.value = value;
this.node = node;
}
}
@Override
public Iterator<Integer> iterator() {
// 匿名内部类 -> 带名字的内部类
return new NodeIterator();
}
// 1.链表头部添加元素
public void addFirst(int value) {
// 1.当链表为空时
// head = new Node(value, null); 因为 head = new Node(value, head);这行代码可以处理链表为空的情况,则注释
// 2.当链表非空时
head = new Node(value, head);
}
// 寻找最后一个节点
private Node findLast() {
// 链表为空
if (head == null) {
return null;
}
Node p;
for(p = head; p.next != null; p = p.next) {
}
return p;
}
// 3.尾部添加元素
private void addLast(int value) {
Node last = findLast();
if (last == null) {
// 头部添加
addFirst(value);
return;
}
last.next = new Node(value, null);
}
// 4.根据索引值查找结点
public Node findNode(int index) {
int i = 0;
for(Node p = head; p != null; p = p.next, i++) {
if (index == i) {
return p;
}
}
// 未找到
return null;
}
// 5.根据index找到对应节点,并返回节点值
public int get(int index) {
Node p = findNode(index);
if (p == null) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
return p.value;
}
// 6.向索引位置插入元素
public void insert(int index, int value) {
if (index == 0) { // 索引为0,向链表头部添加
addFirst(value);
return;
}
// 1.找到插入索引位置的上一个节点
Node p = findNode(index - 1);
// 判断p节点是否为空
if (p == null) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
Node n = p.next;
// p存在的情况
p.next = new Node(value, n);
}
// 7.删除头部节点
public void removeFirst() {
if (head == null) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
// 头节点的下一个节点赋给头
head = head.next;
}
// 8.按索引位置删除节点
public void remove (int index) {
if (index == 0) {
removeFirst();
return;
}
Node p = findNode(index - 1); // 找到待删除节点的上一个节点
if (p == null) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
Node n = p.next; // 待删除节点
if (n == null) {
// 未找到待删除节点
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
p.next = n.next; // 待删除节点的上一个节点p的next指向待删除结点的下一个节点
}
// 6.遍历链表,方法1
public void loop1(Consumer<Integer> consumer) {
Node p = head; // 头节点赋给p
while (p != null) {
consumer.accept(p.value);
p = p.next;
}
}
// 循环遍历,方法2
public void loop2(Consumer<Integer> consumer) {
for(Node p = head; p != null; p = p.next) {
consumer.accept(p.value);
}
}
// 遍历,方法3,使用迭代器,实现接口Iterable
public class NodeIterator implements Iterator<Integer> {
Node p = head;
@Override
public boolean hasNext() { //判断是否有下一个元素
return p != null;
}
@Override
public Integer next() { //返回当前节点值
int value = p.value;
p = p.next;
return value;
}
}
}
测试
public class TestSinglyLinkedList() {
// 尾部添加, 根据索引查找对应节点值测试
@Test
public void test3() {
SinglyLinkedList list = new SinglyLinkedList();
list.addLast(1);
list.addLast(2);
list.addLast(3);
list.addLast(4);
// Assertions.assertIterableEquals(List.of(1, 2, 3, 4), list);
int i = list.get(2);
System.out.println(i); //i = 3
}
@Test
public void test4() {
SinglyLinkedList list = new SinglyLinkedList();
list.addLast(1);
list.addLast(2);
list.addLast(3);
list.addLast(4);
list.insert(2, 5); 向索引为2的位置插入元素5
for(Integer value : list) {
System.out.print(value);
}
}
}
带哨兵的单向链表
// 单项链表
public class SinglyLinkedList implements Iterable<Integer>() {
private Node head = new Node(666, null); // 头指针
// 节点类
private static class Node() {
int value; // 值
Node node; // 下一个节点
public Node(int value, Node node) {
this.value = value;
this.node = node;
}
}
@Override
public Iterator<Integer> iterator() {
// 匿名内部类 -> 带名字的内部类
return new NodeIterator();
}
// 1.链表头部添加元素
public void addFirst(int value) {
insert(0, value);
}
// 寻找最后一个节点
private Node findLast() {
Node p;
for(p = head; p.next != null; p = p.next) {
}
return p;
}
// 3.尾部添加元素
private void addLast(int value) {
Node last = findLast();
last.next = new Node(value, null);
}
// 4.根据索引值查找结点
public Node findNode(int index) {
int i = -1;
for(Node p = head; p != null; p = p.next, i++) {
if (index == i) {
return p;
}
}
// 未找到
return null;
}
// 5.根据index找到对应节点,并返回节点值
public int get(int index) {
Node p = findNode(index);
if (p == null) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
return p.value;
}
// 6.向索引位置插入元素
public void insert(int index, int value) {
// 1.找到插入索引位置的上一个节点
Node p = findNode(index - 1);
// 判断p节点是否为空
if (p == null) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
Node n = p.next;
// p存在的情况
p.next = new Node(value, n);
}
// 7.删除头部节点
public void removeFirst() {
remove(0);
}
// 8.按索引位置删除节点
public void remove (int index) {
Node p = findNode(index - 1); // 找到待删除节点的上一个节点
if (p == null) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
Node n = p.next; // 待删除节点
if (n == null) {
// 未找到待删除节点
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
p.next = n.next; // 待删除节点的上一个节点p的next指向待删除结点的下一个节点
}
// 6.遍历链表,方法1
public void loop1(Consumer<Integer> consumer) {
Node p = head.next; // 头节点赋给p
while (p != null) {
consumer.accept(p.value);
p = p.next;
}
}
// 循环遍历,方法2
public void loop2(Consumer<Integer> consumer) {
for(Node p = head.next; p != null; p = p.next) {
consumer.accept(p.value);
}
}
// 遍历,方法3,使用迭代器,实现接口Iterable
public class NodeIterator implements Iterator<Integer> {
Node p = head.next;
@Override
public boolean hasNext() { //判断是否有下一个元素
return p != null;
}
@Override
public Integer next() { //返回当前节点值
int value = p.value;
p = p.next;
return value;
}
}
}
双向链表–哨兵
public class DoublyLinkedListSentinel implements Iterator<Integer> {
static class Node {
Node prev; // 上一节点
int value; // 值
Node next; // 下一节点
public Node (Node prev, int value, Node next) {
this.prev = prev;
this.value = value;
this.next = next;
}
}
private Node head; //头哨兵节点
private Node tail; //尾哨兵节点
public DoublyLinkedListSentinel () {
head = new Node(null, 666, null);
tail = new Node(null, 888, null);
head.next = tail;
tail.prev = head;
}
// 根据索引位置找节点
private Node findIndex(int index) {
int i = -1;
for(Node p = head; p != tail; p = p.next, i++) {
if (index == i) {
return p;
}
}
return null;
}
// 头部插入元素
public void addFirst(int value) {
insert(0, value);
}
// 1.对应索引位置插入元素
public void insert(int index, int value) {
Node prev = findIndex(index - 1); // 插入索引位置前一个结点
if (prev == null) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
Node next = prev.next; // 成为待插入元素节点的下一个节点
Node node = new Node(prev, value, next);
prev.next = node;
next.prev = node;
}
// 尾部添加元素
public void addLast(int value) {
Node last = tail.prev;
Node added = new Node(last, value, tail);
last.next = added;
tail.prev = added;
}
// 删除最后一个节点
public void removeLast() {
Node removed = tail.prev;
if (removed == head) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
Node prev = removed.prev;
prev.next = tail;
tail.prev = prev;
}
// 删除头节点
public void removeFirst() {
remove(0);
}
// 删除节点
public void remove(int index) {
Node prev = findIndex(index - 1); // 删除索引位置前一个结点
if (prev == null) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
Node next = prev.next.next; // 待删除元素节点的下一个节点
if (next == null) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
prev.next = next;
next.prev = prev;
}
// 迭代器遍历
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
Node p = head.next;
@Override
public boolean hasNext() {
return p != tail;
}
@Override
public Integer next() {
int value = p.value;
p = p.next;
return value;
}
}
}
}
环形链表
双向环形链表带哨兵,哨兵既作为头哨兵,又作为尾哨兵
public class DoublyLinkedListSentinel {
private static class Node {
Node prev; // 上一节点
int value; // 值
Node next; // 下一节点
public Node (Node prev, int value, Node next) {
this.prev = prev;
this.value = value;
this.next = next;
}
}
private Node sentinel = new Node(null, -1, null);
public DoublyLinkedListSentinel () {
sentinel.prev = sentinel;
sentinel.next = sentinel;
}
// 根据索引位置找节点
private Node findIndex(int index) {
int i = -1;
for(Node p = sentinel; p != sentinel; p = p.next, i++) {
if (index == i) {
return p;
}
}
return null;
}
// 头部插入元素
public void addFirst(int value) {
Node a = sentinel;
Node b = sentinel.next;
Node added = new Node(a, value, b);
a.next = added;
b.prev = added;
}
// 1.对应索引位置插入元素
public void insert(int index, int value) {
Node prev = findIndex(index - 1); // 插入索引位置前一个结点
if (prev == null) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
Node next = prev.next; // 成为待插入元素节点的下一个节点
Node node = new Node(prev, value, next);
prev.next = node;
next.prev = node;
}
// 尾部添加元素
public void addLast(int value) {
Node b = sentinel;
Node a = sentinel.prev;
Node added = new Node(a, value, b);
b.prev = added;
a.next = added;
}
// 删除头节点
public void removeFirst() {
Node removed = sentinel.next;
if (removed == sentinel) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
Node a = sentinel;
Node b = removed.next;
a.next = b;
b.prev = a;
}
// 删除最后一个节点
public void removeLast() {
Node removed = sentinel.prev;
if (removed == sentinel) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
Node a = sentinel;
Node b = removed.prev;
a.prev = b;
b.next = a;
}
// 根据节点值查找该节点
public Node findByValue(int value) {
Node p = sentinel.next;
whlie (p != sentinel) {
if (p.value == value) {
return p;
}
p = p.next;
}
return null;
}
// 根据节点值删除节点
public void remove(int vlaue) {
Node removed = findByValue(value);
if (removed == null) {
// 节点为空,抛出异常
threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
}
Node a = removed.prev;
Node b = removed.next;
a.next = b;
b.prev = a;
}
// 迭代器遍历
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
Node p = sentinel.next;
@Override
public boolean hasNext() {
return p != sentinel;
}
@Override
public Integer next() {
int value = p.value;
p = p.next;
return value;
}
}
}
}