简介
双向链表(Double-Linked List)是一种重要的线性数据结构,它在单链表的基础上引入了“前驱指针”,使得每个节点不仅能指向其后续节点,还能回溯到其前一个节点。这一特性赋予了双向链表更灵活的遍历方式和更高效的插入、删除操作。
概念与结构
概念
双向链表,又称双链表,是一种链式存储的数据结构。它由一系列节点组成,每个节点包含两部分:数据域和指针域。数据域用于存储具体的数据元素,而指针域则分为两个,分别称为“后继指针”(next)和“前驱指针”(prev),分别指向当前节点的下一个节点和前一个节点。这种设计使得从双向链表中的任何一个节点出发,都能够方便地访问其前后相邻节点。
结构
在Java中,我们可以定义一个表示双向链表节点的类(如DoubleListNode或Node),其中包含数据成员和对应的指针:
class HerNode {
public int date;
public HeroNode next;// 指向下一个节点,默认为null
public HeroNode pre;// 指向前一个节点,默认为null
}
双向链表的常见操作
class DoubleLinkedList {
private HeroNode2 head = new HeroNode2(0, "", "");
// 获取到第一个节点
public HeroNode2 getHead() {
return head;
}
}
插入操作
双向链表支持在头部、尾部或指定位置插入新节点。以下是在头部插入节点的示例:
/**
按照date的大小进行排序
*/
public void add(HeroNode) {
HeroNode temp = head;
boolean flag = false;
while (temp.next != null) {
if (temp.next.date > heroNode.date) {
//先处理后面的指针
heroNode.next = temp.next;
temp.next.pre = heroNode;
//特别注意:
/*
* 双向链表按循序添加的时候,要先处理新节点的后面的指针,再处理前面的指针
* 原因:
* 1.如果先把新节点和链前表半截连接起来,链表前半截指针指向后链表半截的指针就找不到了
* 2.先把新节点和链表后半截连起来,这样由于新节点是可以直接找到的,
* 所以这样可以将新节点和链表后半截连接起来
* */
break;
} else if(temp.next.date == heroNode.date) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
System.out.println("已存在,添加失败");
} else {//再处理前面的指针或者添加到最后
temp.next = heroNode;
heroNode.pre = temp;
}
}
删除操作
同样,双向链表支持删除头部、尾部或特定位置的节点。以下为删除头节点的示例:
public void del(int date) {
if (head.next == null) {
System.out.println("链表为空");
return;
}
HeroNode2 temp = head.next;
boolean flag = false;
while (temp != null) {
if (temp.date == date) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.date = date;
} else {
System.out.println("没找到" + update.date + "的节点,不能修改");
}
}
查找操作
双向链表可以按值查找节点,但通常不如数组或哈希表高效。以下是一个简单的线性查找示例:
public HeroNode find(int target) {
HeroNode current = head;
while (current != null) {
if (current.data == target) {
return current;
}
current = current.next;
}
return null; // 如果未找到目标节点,返回null
}
遍历操作
双向链表可以正向、反向或双方向遍历。以下为正向遍历打印所有节点值的示例:
public void displayForward() {
HeroNode current = head;
while (current != null) {
System.out.print(current.data + " ");
current = current.next;
}
System.out.println();
}
特殊优势
高效插入与删除
由于双向链表节点具有前驱和后继指针,插入或删除节点时只需更改相邻节点的指针即可,无需像单链表那样可能需要遍历找到前一个节点。特别是在链表中间插入或删除时,双向链表的优势更为明显。
双向遍历
除了常规的从头至尾遍历,双向链表还支持从尾至头遍历,这对于某些应用场景(如LRU缓存淘汰策略、回溯搜索等)非常有用。例如,反向遍历代码如下:
public void displayBackward() {
HeroNode current = tail;
while (current != null) {
System.out.print(current.data + " ");
current = current.prev;
}
System.out.println();
}
总结
双向链表作为一种链式数据结构,结合了单链表的优点(动态分配内存、支持灵活插入删除)并克服了其在某些操作上的局限性(如逆序遍历困难)。通过在节点中增加前驱指针,双向链表实现了高效插入、删除和双向遍历功能。在Java中,通过定义节点类和封装类,并实现相应的增删查遍历方法,可以轻松构建和管理双向链表。尽管在空间复杂度上略高于单链表(每个节点多一个指针),但在需要频繁进行双向操作的场景下,双向链表往往能提供更好的性能。