链表
定义
链表是数据元素的线性集合,其每个元素都指向下一个元素,元素存储上是不连续的。
分类
- 单向链表,每个元素只知道自己的下一个元素是谁。
- 双向链表,每个元素知道自己的上一个元素和下一个元素。
- 循环链表,通常链表尾部节点 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;}}@Overridepublic 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.遍历链表,方法1public void loop1(Consumer<Integer> consumer) {Node p = head; // 头节点赋给pwhile (p != null) {consumer.accept(p.value);p = p.next;}}// 循环遍历,方法2public void loop2(Consumer<Integer> consumer) {for(Node p = head; p != null; p = p.next) {consumer.accept(p.value);}}// 遍历,方法3,使用迭代器,实现接口Iterablepublic class NodeIterator implements Iterator<Integer> {Node p = head;@Overridepublic boolean hasNext() { //判断是否有下一个元素return p != null;}@Overridepublic Integer next() { //返回当前节点值int value = p.value;p = p.next;return value; }}
}
测试
public class TestSinglyLinkedList() {// 尾部添加, 根据索引查找对应节点值测试@Testpublic 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}@Testpublic void test4() {SinglyLinkedList list = new SinglyLinkedList();list.addLast(1);list.addLast(2);list.addLast(3);list.addLast(4);list.insert(2, 5); 向索引为2的位置插入元素5for(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;}}@Overridepublic 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.遍历链表,方法1public void loop1(Consumer<Integer> consumer) {Node p = head.next; // 头节点赋给pwhile (p != null) {consumer.accept(p.value);p = p.next;}}// 循环遍历,方法2public void loop2(Consumer<Integer> consumer) {for(Node p = head.next; p != null; p = p.next) {consumer.accept(p.value);}}// 遍历,方法3,使用迭代器,实现接口Iterablepublic class NodeIterator implements Iterator<Integer> {Node p = head.next;@Overridepublic boolean hasNext() { //判断是否有下一个元素return p != null;}@Overridepublic 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;}// 迭代器遍历@Overridepublic Iterator<Integer> iterator() {return new Iterator<Integer>() {Node p = head.next;@Overridepublic 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;}// 迭代器遍历@Overridepublic Iterator<Integer> iterator() {return new Iterator<Integer>() {Node p = sentinel.next;@Overridepublic boolean hasNext() {return p != sentinel;}@Override public Integer next() {int value = p.value;p = p.next;return value;}}}
}