编程小白们集合!上次咱们聊了单项链表这个 “单向小火车”,今天再来认识一位它的 “兄弟”—— 双向链表。数据世界里的 “双向小火车”,它叫双向链表。听名字是不是感觉有点高大上?别担心,看完这篇文章,保准你能轻松拿捏它!
想象一下,你和一群小伙伴手拉手排成一列,每个人只能看到前面的人,要是想知道后面发生了什么,还得一个个传话,这就是单向链表。而双向链表就不一样啦,每个人不仅能拉住前面小伙伴的手,还能拉住后面小伙伴的手,前后信息交流超方便!这样不管从前面还是后面找起,都能快速找到你想要的小伙伴,双向链表处理数据的原理就和这差不多。
双向链表长啥样?
在双向链表中,每个数据元素都被封装在一个 “小房子” 里,这个 “小房子” 我们叫它节点。每个节点都有三个 “小房间”:一个用来存放数据,另外两个分别用来存放下一个节点和上一个节点的 “地址”(在 Java 里其实是引用)。就好比每个小伙伴身上都揣着前面和后面小伙伴家的门牌号,想去谁家串门都超方便!
用 Java 代码来定义一个双向链表的节点长这样:
class Node {
int data;
Node prev;
Node next;
Node(int data) {
this.data = data;
this.prev = null;
this.next = null;
}
}
在这段代码里,data 就是存放数据的 “房间”,prev 是指向前一个节点的 “门牌号”,next 则是指向后一个节点的 “门牌号” 。构造函数 Node(int data) 就像是给每个 “小房子” 分配数据,顺便把前后 “门牌号” 先设为空,等着后续 “入住” 其他节点。
双向链表的基本操作
1. 创建双向链表
创建双向链表就像是召集一群小伙伴手拉手站好。我们先创建几个节点,然后让它们前后 “握手” 连接起来。
class DoublyLinkedList {
Node head;
// 创建双向链表
void createList() {
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
// 连接节点
node1.next = node2;
node2.prev = node1;
node2.next = node3;
node3.prev = node2;
head = node1;
}
}
在 createList 方法里,我们先创建了三个节点,分别存着数字 1、2、3。接着让 node1 的 next 指向 node2,node2 的 prev 指向 node1,以此类推,最后把 head 设为 node1,这样一个简单的双向链表就创建好啦!
2. 插入节点
插入节点就好比在已经站好队的小伙伴中间,加入一个新的小伙伴。插入的位置不同,操作也有点不一样,咱们以在链表头部插入为例。
// 在头部插入节点
void insertAtHead(int data) {
Node newNode = new Node(data);
if (head == null) {
head = newNode;
} else {
newNode.next = head;
head.prev = newNode;
head = newNode;
}
}
先创建一个新节点 newNode 存着要插入的数据。如果链表是空的(head == null),那这个新节点就是链表的头节点。要是链表已经有节点了,就让新节点的 next 指向原来的头节点 head,原来头节点的 prev 指向新节点,最后把 head 更新为新节点,这样新节点就成功 “插队” 到最前面啦!
3. 删除节点
删除节点就像把队伍里的某个小伙伴 “移除” 出去。我们以删除指定数据的节点为例。
// 删除指定数据的节点
void deleteNode(int data) {
Node current = head;
while (current != null) {
if (current.data == data) {
if (current.prev != null) {
current.prev.next = current.next;
} else {
head = current.next;
}
if (current.next != null) {
current.next.prev = current.prev;
}
return;
}
current = current.next;
}
}
先从链表头节点开始,一个个找,看看哪个节点存的数据和我们要删除的数据一样。如果找到了,就分情况处理:要是这个节点前面还有其他节点,就让前面节点的 next 跳过当前节点,直接指向下一个节点;要是它就是头节点,那就把 head 更新为它后面的节点。同时,还要处理好它后面节点的 prev,让其指向前面的节点,这样就把这个节点从链表中 “踢出去” 啦!
4. 遍历双向链表
遍历双向链表就像是从队伍的一头走到另一头,挨个和小伙伴打招呼。因为双向链表可以从前往后,也能从后往前遍历,咱们试试从前往后遍历。
// 遍历双向链表
void traverse() {
Node current = head;
while (current != null) {
System.out.print(current.data + " ");
current = current.next;
}
}
从链表头节点 head 开始,只要当前节点不是空的,就打印出它存的数据,然后让 current 指向下一个节点,直到把整个链表的节点都 “看” 一遍,这样就完成了遍历操作。
双向链表的优缺点
双向链表这么灵活,是不是就完美无缺了呢?当然不是!它也有自己的优缺点。
- 优点:可以双向遍历,不管从前往后还是从后往前找数据都很方便;在插入和删除节点时,操作相对灵活,因为能直接找到前后节点。
- 缺点:每个节点都要多存一个指向前一个节点的引用,会占用更多的内存空间;插入和删除节点时,需要处理更多的指针关系,代码稍微复杂一些。
好啦,关于双向链表的知识就介绍到这里!现在你是不是觉得双向链表也没那么难理解啦?以后在编程中遇到需要频繁插入、删除数据,还得前后查找的场景,就可以考虑让双向链表这个 “双向小火车” 来帮忙啦!快去用 Java 代码实践一下吧,相信你很快就能玩转它!