在上一篇:单向链表中,可以发现单向链表有一些缺点
- 单向链表,查找的方向只能是一个方向,只能从前向后查找
- 单向链表不能自我删除,需要先找到被删除节点的前一个节点front,使用front节点来辅助删除,不方便
双向链表就能够解决这些问题
- 如图1,双向链表包括两个指针域,previous(简写prev)、next
-
因为双向链表有两个指针域,所以内存结构如图2
图2.双向链表的内存结构 -
常见操作
- 链表尾部插入新节点原理示意图
- 链表中部插入新节点原理示意图
中部插入新节点
// 先找到A节点
A.next.prev = C;
C.next = A.next;
A.next = C;
C.prev = A;
- 删除节点原理示意图
C.next.prev = C.prev;
C.prev.next = C.next;
代码示例
1.因为链表由一个一个的节点组成,因此需要定义一个节点类(Node)。假设属性包括id、name(这些是节点的数据域),和prev、next(指针域)。
Node类
/**
* 定义一个Node类,一个Node对象就是一个节点
*/
class Node2 {
private Node prev; // 指向前一个节点
private int id; // id,唯一
private String name; // 姓名
private Node next; // 指向后一个节点
// 省略setter、getter、构造器
}
2.定义一个DubboLinkedList 双向链表类,DubboLinkedList 类包括一个成员变量head,是链表的头结点
DubboLinkedList 类
/**
* 定义DubboLinkedList类表示双向链表
*/
class DubboLinkedList {
// 头结点,头结点不保存数据
Node head = new Node();
// 获取链表的最后那个节点
public Node getLast() {
// temp变量来保存链表的最后那个节点
Node temp = head;
while (temp.getNext() != null) {
temp = temp.getNext();
}
// 循环结束时,temp就是最后那个节点
return temp;
}
// 获取链表的第一个节点(不是头结点)
public Node getFirst() {
return head.getNext();
}
// 根据id查找指定节点
public Node find(int id) {
Node temp = head.getNext();
while (temp != null) {
// 本示例中约定id相同就是同一个节点
if (temp.getId() == id) {
break;
}
// temp后移
temp = temp.getNext();
}
return temp;
}
// 正序遍历链表
public void list() {
// 判空
if (head.getNext() == null) {
System.out.println("链表为空,无数据");
return;
}
Node temp = head.getNext();
while (temp != null) {
System.out.println(temp);
// temp后移
temp = temp.getNext();
}
}
// 倒序遍历链表
public void reverseOrderList() {
// 判空
if (head.getNext() == null) {
System.out.println("链表为空,无数据");
return;
}
// 先得到最后那个节点
Node temp = getLast();
while (temp != null) {
System.out.println(temp);
temp = temp.getPrev();
if (temp.getPrev() == null) {
// 如果temp.getPrev()==null,说明当前temp是头结点,不打印头结点
break;
}
}
}
// 添加新节点到链表尾部
public void append(Node node) {
Node last = getLast();
// 添加新节点
last.setNext(node);
node.setPrev(last);
}
// 插入节点到指定节点后
public void insertAfter(Node node, Node newNode) {
// 先根据id找到这个节点
Node beforeNode = find(node.getId());
// 插入节点
beforeNode.getNext().setPrev(newNode);
newNode.setNext(beforeNode.getNext());
beforeNode.setNext(newNode);
newNode.setPrev(beforeNode);
}
// 删除指定节点,并返回被删除节点
public Node delete(Node node) {
if (head.getNext() == null) {
System.out.println("链表为空");
return null;
}
// 找到被删除节点
Node deleteNode = find(node.getId());
if (deleteNode == null) {
System.out.println("没有找到指定节点");
return deleteNode;
}
//删除节点
deleteNode.getPrev().setNext(deleteNode.getNext());
//如果被删除的节点不是最后那个节点才执行, 因为最后的节点的next指针为null,不判断可能产生空指针异常
if (deleteNode.getNext() != null) {
deleteNode.getNext().setPrev(deleteNode.getPrev());
}
return deleteNode;
}
// 修改节点
public boolean update(Node node) {
// 先找到节点
Node updateNode = find(node.getId());
if (updateNode == null) {
return false;
}
updateNode.setName(node.getName());
return true;
}
}
3.测试
public class DubboLinkedListDemo {
public static void main(String[] args) {
// 创建链表
DubboLinkedList dubboLinkedList = new DubboLinkedList();
// 创建节点
Node node1 = new Node(1, "周星驰");
Node Node = new Node(2, "刘德华");
Node node3 = new Node(3, "古天乐");
Node node4 = new Node(4, "张家辉");
Node node5 = new Node(5, "尼古拉斯赵四");
// 添加节点
dubboLinkedList.append(node1);
dubboLinkedList.append(Node);
dubboLinkedList.append(node3);
dubboLinkedList.append(node4);
dubboLinkedList.append(node5);
// 正序遍历链表
System.out.println("正序遍历链表:");
dubboLinkedList.list();
System.out.println("倒序遍历链表:");
dubboLinkedList.reverseOrderList();
// 在id为2的节点后面添加一个新节点
dubboLinkedList.insertAfter(new Node(2, null), new Node(6, "范迪塞尔"));
System.out.println("在id为2的节点后添加新节点后遍历:");
dubboLinkedList.list();
dubboLinkedList.delete(new Node(5, null));
System.out.println("删除id为5的节点后遍历:");
dubboLinkedList.list();
// 查找id为4的节点
Node node = dubboLinkedList.find(4);
System.out.println("查找id为4的节点:" + node);
//修改id为6的节点
dubboLinkedList.update(new Node(6,"尼古拉斯赵四"));
System.out.println("修改id为6的节点:");
dubboLinkedList.list();
}
}
4.控制台打印
正序遍历链表:
Node{id=1, name='周星驰'}
Node{id=2, name='刘德华'}
Node{id=3, name='古天乐'}
Node{id=4, name='张家辉'}
Node{id=5, name='尼古拉斯赵四'}
倒序遍历链表:
Node{id=5, name='尼古拉斯赵四'}
Node{id=4, name='张家辉'}
Node{id=3, name='古天乐'}
Node{id=2, name='刘德华'}
Node{id=1, name='周星驰'}
在id为2的节点后添加新节点后遍历:
Node{id=1, name='周星驰'}
Node{id=2, name='刘德华'}
Node{id=6, name='范迪塞尔'}
Node{id=3, name='古天乐'}
Node{id=4, name='张家辉'}
Node{id=5, name='尼古拉斯赵四'}
删除id为5的节点后遍历:
Node{id=1, name='周星驰'}
Node{id=2, name='刘德华'}
Node{id=6, name='范迪塞尔'}
Node{id=3, name='古天乐'}
Node{id=4, name='张家辉'}
查找id为4的节点:Node{id=4, name='张家辉'}
修改id为6的节点:
Node{id=1, name='周星驰'}
Node{id=2, name='刘德华'}
Node{id=6, name='尼古拉斯赵四'}
Node{id=3, name='古天乐'}
Node{id=4, name='张家辉'}