一、双向链表介绍

  双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表

双向链表图示

  

java 单向链表和双向链表 单向链表与双向链表_System

双向链表与单链表对比

  1、单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找。

  2、单向链表不能自我删除,需要靠辅助节点 ,而双向链表,则可以自我删除。所以前面我们单链表删除节点时,总是找到 temp, temp 是待删除节点的前一个节点。

二、java代码实现双向链表的增删改查

2.1 应用案例

  使用双向链表实现 – 水浒英雄排行榜管理完成对英雄人物的增删改查操作。

2.2 编写HeroNode2类

1 public class HeroNode2 {
 2     public int no;
 3     public String name;
 4     public String nickname;
 5     public HeroNode2 next; // 指向下一个节点,默认为null
 6     public HeroNode2 pre; // 指向上一个节点,默认为null
 7 
 8     // 构造器
 9     public HeroNode2(int no, String name, String nickname) {
10         this.no = no;
11         this.name = name;
12         this.nickname = nickname;
13     }
14 
15     // 为了显示方法,重写toString方法
16     @Override
17     public String toString() {
18         return "HeroNode2{" +
19                 "no=" + no +
20                 ", name='" + name + '\'' +
21                 ", nickname='" + nickname + '\'' +
22                 '}';
23     }
24 }

2.3 编写DoubleLinkedList类

1 /**
  2  * @description: 创建一个双向链表的类
  3  * @author: hyr
  4  * @time: 2020/1/19 9:48
  5  */
  6 public class DoubleLinkedList {
  7     // 先初始化一个头节点,头节点不要动,不存放具体的数据
  8     private HeroNode2 head = new HeroNode2(0, "", "");
  9 
 10     // 返回头节点
 11     public HeroNode2 getHead() {
 12         return head;
 13     }
 14 
 15     // 显示链表
 16     public void list() {
 17         // 判断链表是否为空
 18         if (head.next == null) {
 19             System.out.println("链表为空");
 20             return;
 21         }
 22         // 因为头节点,不能动,因此我们需要一个辅助变量来遍历
 23         HeroNode2 temp = head.next;
 24         while (true) {
 25             // 判断是否到链表最后
 26             if (temp == null) {
 27                 break;
 28             }
 29             // 输出节点的信息
 30             System.out.println(temp);
 31 
 32             // 将temp后移
 33             temp = temp.next;
 34         }
 35     }
 36 
 37     // 添加一个节点到双向链表的最后
 38     public void add(HeroNode2 heroNode) {
 39         // 因为头节点不能动,因此我们需要一个辅助变量temp
 40         HeroNode2 temp = head;
 41         // 遍历链表,找到最后
 42         while (true) {
 43             // 找到链表的最后
 44             if (temp.next == null) {
 45                 break;
 46             }
 47             // 如果没有找到,将temp后移
 48             temp = temp.next;
 49         }
 50         // 当退出循环时,temp就指向了链表的最后
 51         // 形成一个双向链表
 52         temp.next = heroNode;
 53         heroNode.pre = temp;
 54     }
 55 
 56     // 按照顺序添加
 57     public void add1(HeroNode2 heroNode) {
 58         // 因为头节点不能动,因此我们仍然通过一个辅助指针(变量)来帮助找到添加的位置
 59         // 因为单链表,因为我们找的 temp 是位于添加位置的前一个节点,否则插入不了
 60         HeroNode2 temp = head;
 61         // 标志添加的编号是否存在,默认为false
 62         boolean flag = false;
 63         while (true) {
 64             // 说明 temp 已经在链表的最后
 65             if (temp.next == null) {
 66                 break;
 67             }
 68             // 位置找到,就在 temp 的后面插入
 69             if (temp.next.no > heroNode.no) {
 70                 break;
 71             }
 72             // 说明希望添加的 heroNode 的编号已然存在
 73             else if (temp.next.no == heroNode.no) {
 74                 //说明编号存在
 75                 flag = true;
 76                 break;
 77             }
 78             // 后移,遍历当前链表
 79             temp = temp.next;
 80         }
 81         // 判断 flag 的值
 82         // 不能添加,说明编号存在
 83         if (flag) {
 84             System.out.printf("准备插入的英雄的编号%d已经存在了,不能加入\n", heroNode.no);
 85         } else {
 86             // 插入到链表中,temp的后面
 87             // 这里需要判断一下是不是最后一个节点,需要不同的操作。
 88             if (temp.next != null) {
 89                 heroNode.next = temp.next;
 90                 heroNode.next.pre = heroNode;
 91             }
 92             temp.next = heroNode;
 93             heroNode.pre = temp;
 94         }
 95     }
 96 
 97     // 修改一个节点的内容,可以看到双向链表的节点内容修改和单向链表一样
 98     // 只是节点类型修改为HeroNode2
 99     public void update(HeroNode2 newHeroNode) {
100         // 判断是否为空
101         if (head.next == null) {
102             System.out.println("链表为空");
103             return;
104         }
105         // 找到需要修改的节点,根据no编号
106         // 定义一个辅助变量
107         HeroNode2 temp = head.next;
108         boolean flag = false; // 表示是否找到该节点
109         while (true) {
110             if (temp == null) {
111                 break; // 已经遍历完链表
112             }
113             if (temp.no == newHeroNode.no) {
114                 // 找到
115                 flag = true;
116                 break;
117             }
118             temp = temp.next;
119         }
120         // 根据flag判断是否找到要修改的节点
121         if (flag) {
122             temp.name = newHeroNode.name;
123             temp.nickname = newHeroNode.nickname;
124         } else {
125             System.out.printf("没有找到编号为%d的节点,不能修改\n", newHeroNode.no);
126         }
127     }
128 
129     // 从双向链表中删除一个节点
130     // 说明
131     // 1、对于双向链表,我们可以直接找到要删除的这个节点
132     // 2、找到后,自我删除即可
133     public void del(int no) {
134         // 判断当前链表是否为空
135         if (head.next == null) {
136             System.out.println("链表为空,无法删除");
137             return;
138         }
139         HeroNode2 temp = head.next; // 辅助变量
140         boolean flag = false; // 标志是否找到待删除节点
141         while (true) {
142             if (temp == null) {
143                 // 已经到了链表的最后
144                 break;
145             }
146             if (temp.no == no) {
147                 // 找到的待删除节点的前一个节点temp
148                 flag = true;
149                 break;
150             }
151             temp = temp.next; // temp后移,遍历
152         }
153         // 判断flag
154         if (flag) { // 找到
155             // 可以删除
156             temp.pre.next = temp.next;
157             // 如果是最后一个节点,就不需要执行下面这句话,否则出现空指针
158             if (temp.next != null) {
159                 temp.next.pre = temp.pre;
160             }
161         } else {
162             System.out.printf("要删除的%d节点不存在\n", no);
163         }
164     }
165 }

2.4 编写测试类DoubleLinkedDemo

1 /**
 2  * @description: 使用双向链表完成对水浒英雄榜的增删改查
 3  * @author: hyr
 4  * @time: 2020/1/19 18:25
 5  */
 6 public class DoubleLinkedDemo {
 7     public static void main(String[] args) {
 8         // 先创建节点
 9         HeroNode2 hero1 = new HeroNode2(1, "宋江", "及时雨");
10         HeroNode2 hero2 = new HeroNode2(2, "卢俊义", "玉麒麟");
11         HeroNode2 hero3 = new HeroNode2(3, "吴用", "智多星");
12         HeroNode2 hero4 = new HeroNode2(4, "林冲", "豹子头");
13 
14         // 创建一个双向链表
15         DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
16 
17 //        // 1、按照no順序向双向链表中增添数据
18 //        doubleLinkedList.add1(hero1);
19 //        doubleLinkedList.add1(hero2);
20 //        doubleLinkedList.add1(hero3);
21 //        doubleLinkedList.add1(hero4);
22 //        // 查看一下双向链表中的数据
23 //        System.out.println("增添完数据后的双向链表为:");
24 //        doubleLinkedList.list();
25 //        System.out.println();
26 
27         // 2、按照no順序向双向链表中增添数据
28         doubleLinkedList.add1(hero1);
29         doubleLinkedList.add1(hero3);
30         doubleLinkedList.add1(hero2);
31         doubleLinkedList.add1(hero4);
32         // 查看一下双向链表中的数据
33         System.out.println("增添完数据后的双向链表为:");
34         doubleLinkedList.list();
35         System.out.println();
36 
37         // 3、修改
38         HeroNode2 newHeroNode = new HeroNode2(4,"鲁智深", "花和尚");
39         doubleLinkedList.update(newHeroNode);
40         System.out.println("修改后的双向链表为:");
41         doubleLinkedList.list();
42         System.out.println();
43 
44         // 4、删除
45         doubleLinkedList.del(3);
46         System.out.println("删除后的链表情况为:");
47         doubleLinkedList.list();
48     }
49 }