双向链表
基础
- 双向链表是一种数据结构,它由一系列节点组成,每个节点包含两个指针,一个指向前一个节点,一个指向后一个节点。
- 双向链表可以在任何位置添加、删除和修改节点,因为每个节点都可以直接访问前一个和后一个节点。
- 双向链表的头节点和尾节点分别是第一个节点和最后一个节点,它们都没有前驱和后继节点。
- 双向链表的每个节点通常包含两个部分:数据部分和指针部分。数据部分用于存储节点的值,指针部分用于存储前驱和后继节点的地址。
- 双向链表的实现需要定义一个节点类,包含节点值、前驱指针和后继指针等属性。同时,需要定义一个双向链表类,包含头节点和尾节点等属性,以及一系列方法来实现双向链表的各种操作。
- 双向链表在Java中有多种实现方式,例如LinkedList类就是使用双向链表实现的。在实际开发中,需要根据具体的需求来选择适当的数据结构。
代码实现双向链表的增删改查
public class DoubleNode {
int val;
DoubleNode prev;
DoubleNode next;
public DoubleNode(int val) {
this.val = val;
this.prev = null;
this.next = null;
}
}
public class DoubleLinkedList {
private DoubleNode head;
private DoubleNode tail;
private int size;
public DoubleLinkedList() {
head = null;
tail = null;
size = 0;
}
public void addFirst(int val) {
DoubleNode node = new DoubleNode(val);
if (head == null) {
head = tail = node;
} else {
node.next = head;
head.prev = node;
head = node;
}
size++;
}
public void addLast(int val) {
DoubleNode node = new DoubleNode(val);
if (tail == null) {
head = tail = node;
} else {
tail.next = node;
node.prev = tail;
tail = node;
}
size++;
}
public void addAtIndex(int index, int val) {
if (index < 0 || index > size) return;
if (index == 0) {
addFirst(val);
return;
}
if (index == size) {
addLast(val);
return;
}
DoubleNode cur = head;
for (int i = 0; i < index - 1; i++) {
cur = cur.next;
}
DoubleNode node = new DoubleNode(val);
node.prev = cur;
node.next = cur.next;
cur.next.prev = node;
cur.next = node;
size++;
}
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) return;
if (index == 0) {
head = head.next;
if (head != null) head.prev = null;
if (head == null) tail = null;
size--;
return;
}
if (index == size - 1) {
tail = tail.prev;
tail.next.prev = null;
tail.next = null;
if (tail == null) head = null;
size--;
return;
}
DoubleNode cur = head;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
cur.prev.next = cur.next;
cur.next.prev = cur.prev;
size--;
}
public int get(int index) {
if (index < 0 || index >= size) return -1;
if (index < size / 2) {
DoubleNode cur = head;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
return cur.val;
} else {
DoubleNode cur = tail;
for (int i = size - 1; i > index; i--) {
cur = cur.prev;
}
return cur.val;
}
}
}
定义了一个双向链表的节点类DoubleNode,包括节点的值val和前节点prev和后节点next。然后定义了一个双向链表类DoubleLinkedList,包括头结点head、尾结点tail和链表长度size。这个类实现了头插法addFirst、尾插法addLast、在指定位置插入元素addAtIndex、删除指定位置元素deleteAtIndex和获取指定位置元素get等操作。
优缺点
双向链表是一种数据结构,它与单向链表不同,每个节点包含两个指针,一个指向前一个节点,一个指向后一个节点。
优点:
- 可以在任意位置快速添加、删除和修改节点,因为每个节点都可以直接访问前一个和后一个节点,不需要像单向链表那样遍历到需要操作的节点的前一个节点。
- 双向链表可以从前向后或从后向前遍历整个链表,这使得某些特定场景下的操作更加方便,例如需要从链表尾部开始遍历或倒序输出链表中的节点。
缺点:
- 双向链表需要额外的指针空间来存储前驱和后继节点,因此占用的存储空间较大,比单向链表更加浪费空间。
- 双向链表的每个节点都需要维护前驱和后继指针,因此在插入、删除和修改节点时需要更新相应的指针,这会增加一定的时间和空间复杂度。
- 双向链表的实现相对于单向链表的实现要复杂一些,需要维护额外的指针,并且容易出现指针问题,例如指针错误、空指针异常等,对于开发和调试都带来一定的难度。
单双链表的区别
- 节点结构不同
单链表的节点结构包含两个部分:数据部分和指针部分,指针部分只包含指向后继节点的指针,不包含指向前驱节点的指针。
双链表的节点结构包含三个部分:数据部分和两个指针部分,一个指向前驱节点,一个指向后继节点。
- 遍历方向不同
在单链表中,只能从头节点开始遍历整个链表,每个节点只能访问它的后继节点,无法访问它的前驱节点。
在双链表中,可以从任意节点开始遍历整个链表,每个节点既可以访问它的前驱节点,也可以访问它的后继节点。
- 操作复杂度不同
在单链表中,插入和删除节点需要找到目标节点的前驱节点,需要遍历整个链表,时间复杂度为O(n)。而在双链表中,可以直接访问目标节点的前驱和后继节点,因此插入和删除节点的时间复杂度为O(1)。
- 存储空间不同
由于单链表每个节点只包含一个指针,因此占用的存储空间比双链表要小一半。而双链表每个节点需要额外的指向前驱节点的指针,因此占用的存储空间比单链表要大一些。
总的来说,单链表和双链表都有其优缺点,应根据具体情况选择合适的数据结构。单链表空间占用小,但插入和删除节点的复杂度较高;双链表插入和删除节点的复杂度低,但占用的存储空间相对较大。