双向链表增删改查分析图解

双向链表应用

使用带head头的双向链表实现-水浒英雄排行榜

单向链表的缺点分析:

  1. 单向链表,查找的方向只能是一个方向,双向链表可以向前或者向后查找;
  2. 单向链表不能自我删除,需要依靠辅助节点,而双向链表可以自我删除;(单向链表删除节点时,总是找到temp的下一个节点来删除)

双向链表增删改查分析

分析图解

image-20220928105852508

思路分析

  1. 遍历方式和单链表一样,只是可以向前查找,也可以向后查找;
  2. 添加(例如添加到双链表末尾)
    • 先找到双向链表的最后的这个节点;
    • temp.next=newHeroNode;
    • newHeroNode.pre = temp;
  3. 修改和单向链表基本一致;
  4. 删除
    • 因为是双向链表,可以实现自我删除某个节点;
    • 直接找到要删除的节点,例如:temp;
    • temp.pre.next = temp.next;
    • temp.next.pre = temp.pre;

双向链表增删改查代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package com.jokerdig.linkedlist;

/**
* @author Joker大雄
* @data 2022/9/28 - 11:03
**/
public class DoubleLinkedListDemo {
public static void main(String[] args) {
}
}
class DoubleLinkedList{
// 初始化一个head节点,一般不要动,也不存放数据
private HeroNodeDouble head = new HeroNodeDouble(0,"","");

// 返回头节点
public HeroNodeDouble getHead() {
return head;
}

// 显示链表(遍历)
public void list(){
// 判断链表是否为空
if(head.next == null){
System.out.println("链表为空");
return;
}
// 头节点不动,因此我们需要一个辅助变量来遍历
HeroNodeDouble temp = head.next;
while(true){
// 判断是否到链表末尾
if(temp == null){
break;
}
// 输出节点的信息
System.out.println(temp);
// 输出信息后,需要将temp后移
temp = temp.next;
}
}

// 添加节点到双向链表
public void add(HeroNodeDouble heroNode){
// head不动,因此我们呢需要复制变量temp
HeroNodeDouble temp = head;
// 遍历链表,找到最后
while(true){
// 当next为null,说明到最后了
if(temp.next==null){
break;
}
// 如果没有找到,就将temp后移
temp = temp.next;
}
// 当结束while循环时,temp指向链表最后
// temp.next指向要添加的新节点
temp.next = heroNode;
// 把添加的新节点heroNode.pre指向temp
heroNode.pre = temp;
}

// 修改双向链表的节点
public void update(HeroNodeDouble newHeroNode){
// 判断是否为空
if(head.next == null){
System.out.println("链表为空");
return;
}
// 找到需要修改的节点,根据no编号
// 定义一个辅助变量
HeroNodeDouble temp = head.next;
boolean flag = false; // 表示是否找到该节点
while(true){
if(temp == null){
break; // 到链表遍历完毕
}
if(temp.no == newHeroNode.no){
// 找到要修改的节点
flag = true;
break;
}
temp = temp.next;
}
// 根据flag判断是否找到了要修改的节点
if(flag){
// 进行修改
temp.name = newHeroNode.name;
temp.nickname = newHeroNode.nickname;
}else{
// 没有找到该节点
System.out.println("编号为:"+newHeroNode.no+"的英雄未在链表中找到");
}
}

// 删除双向链表中一个节点
// 对于双向链表,我们可以直接找到要删除的节点,找到后自我删除
public void delete(int deleteNo){
// 判断当前链表是否为空
if(head.next == null){
System.out.println("链表为空,无法删除");
return;
}
// 辅助变量(指针)
HeroNodeDouble temp = head.next;
boolean flag = false;//是否找到
while(true){
if(temp == null){
// 到链表末尾节点的next
break;
}
if(temp.no == deleteNo){
// 找到了待删除的节点
flag = true;
break;
}
temp = temp.next;
}
if(flag){
// 找到了,双向链表来删除
// 待删除节点的前一个节点指向待删除节点后一个节点
temp.pre.next = temp.next;
// 如果删除的是最后一个节点,就不需要指向下方的步骤,否则会出现空指针异常
// 待删除节点的后一个节点的pre指向待删除节点的前一个节点
if(temp.next!=null){
temp.next.pre = temp.pre;
}
}else{
System.out.println("要删除的节点"+deleteNo+"不存在");
}
}
}

// 创建HeroNode,每个HeroNode对象就是一个节点
class HeroNodeDouble{
public int no;
public String name;
public String nickname;
public HeroNodeDouble next; // 指向下一个节点
public HeroNodeDouble pre; // 指向前一个节点

// 构造器
public HeroNodeDouble(int no,String name,String hickname){
this.no = no;
this.name = name;
this.nickname = hickname;
}

// toString
@Override
public String toString() {
return "HeroNodeDouble{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname +
'}';
}
}

双向链表功能测试

功能测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.jokerdig.linkedlist;

/**
* @author Joker大雄
* @data 2022/9/28 - 11:03
**/
public class DoubleLinkedListDemo {
public static void main(String[] args) {
System.out.println("========双向链表的测试========");
// 创建节点
HeroNodeDouble hero1 = new HeroNodeDouble(1,"宋江","及时雨");
HeroNodeDouble hero2 = new HeroNodeDouble(2,"卢俊义","玉麒麟");
HeroNodeDouble hero3 = new HeroNodeDouble(3,"吴用","智多星");
HeroNodeDouble hero4 = new HeroNodeDouble(4,"武松","行者");
HeroNodeDouble hero5 = new HeroNodeDouble(5,"林冲","豹子头");
// 创建一个双向链表对象
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
// 添加测试节点对象
doubleLinkedList.add(hero1);
doubleLinkedList.add(hero2);
doubleLinkedList.add(hero3);
doubleLinkedList.add(hero4);
doubleLinkedList.add(hero5);

// 遍历测试
doubleLinkedList.list();

// 修改测试2号
doubleLinkedList.update(new HeroNodeDouble(2,"公孙胜","入云龙"));
System.out.println("========修改节点后的双向链表========");
doubleLinkedList.list();

// 测试删除3号
doubleLinkedList.delete(3);
System.out.println("========删除节点后的双向链表========");
doubleLinkedList.list();
}
}
// 双向链表增删改查功能,具体看上方
class DoubleLinkedList{...}
// 创建HeroNode,每个HeroNode对象就是一个节点
class HeroNodeDouble{...}

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
========双向链表的测试========
HeroNodeDouble{no=1, name='宋江', nickname='及时雨}
HeroNodeDouble{no=2, name='卢俊义', nickname='玉麒麟}
HeroNodeDouble{no=3, name='吴用', nickname='智多星}
HeroNodeDouble{no=4, name='武松', nickname='行者}
HeroNodeDouble{no=5, name='林冲', nickname='豹子头}
========修改节点后的双向链表========
HeroNodeDouble{no=1, name='宋江', nickname='及时雨}
HeroNodeDouble{no=2, name='公孙胜', nickname='入云龙}
HeroNodeDouble{no=3, name='吴用', nickname='智多星}
HeroNodeDouble{no=4, name='武松', nickname='行者}
HeroNodeDouble{no=5, name='林冲', nickname='豹子头}
========删除节点后的双向链表========
HeroNodeDouble{no=1, name='宋江', nickname='及时雨}
HeroNodeDouble{no=2, name='公孙胜', nickname='入云龙}
HeroNodeDouble{no=4, name='武松', nickname='行者}
HeroNodeDouble{no=5, name='林冲', nickname='豹子头}

Process finished with exit code 0