一、双向链表
双向链表与单向链表相比,多了一个前驱 prev,解决了单向链表很多缺点:单向链表的每个节点仅存储后继节点地址,这导致其在实际应用中存在明显局限:
遍历方向单一:只能从表头(head)向表尾(tail)遍历,无法反向查找。 删除操作低效:若要删除指定节点,需先从头遍历找到其前驱节点,时间复杂度为 O(n)。 表尾操作不便:无法直接获取表尾节点的前驱,若需在表尾插入后修改前驱指针,需重新遍历。
双向链表通过在节点中增加前驱节点地址域,解决了以上问题,所以在实际开发中,使用更多的是双向链表。
二、双向链表的模拟实现
1. 节点类
val 数据,next 后继,prev 前驱,双向链表的前驱节点,可以找到前一个节点,为了方便从后往前遍历,我们使用 last 标记最后一个节点。
static class ListNode {
public int val;
public ListNode next;
public ListNode prev;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;
public ListNode last;
2. 头插法
先创建一个新节点 node,如果链表为空,head 和 last 都指向新节点,当链表不为空时,让新节点的 next 指向头节点(先绑定后面),然后将头节点的前驱 prev 赋值为新节点的地址,最后让 head 指向新的节点。
public void addFirst(int data) {
ListNode node = new ListNode(data);
if (head == null) {
head = node;
last = node;
} else {
node.next = head;
head.prev = node;
head = node;
}
}
3. 尾插法
跟头插法一样,创建一个节点,判断链表是否为空,为空就让 head 和 last 都指向新节点,当链表不为空时,让最后一个节点的 next 指向新的节点(先绑定),然后让新节点的前驱指向前一个节点的地址,最后让 last 指向新节点。
public void addLast( data) {
(data);
(head == ) {
head = node;
last = node;
} {
last.next = node;
node.prev = last;
last = node;
}
}


