双向链表


应用实例

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

  • 单向链表的缺点

    • 单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找
    • 单向链表不能自我删除,需要靠辅助节点,而双向链表,则可以自我删除,所以前面我们单链表删除时节点,总是找到temp,temp是待删除节点的前一个节点(认真体会).
  • 双向链表遍历,添加,修改和删除的思路

image-20220711203520468

  • 遍历

    • 方法和单链表一样,只是可以向前,也可以向后查找
  • 添加(默认添加到双向链表的最后)

    1. 先找到双向链表的最后这个节点
    2. temp.next = newHeroNode
    3. newHeroNode.pre = temp
  • 添加(按排名添加)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    if (temp.next!=null){
    heroNode.next=temp.next;
    temp.next.pre=heroNode;
    temp.next=heroNode;
    heroNode.pre=temp;
    }else {
    temp.next = heroNode;//后指前
    heroNode.pre = temp;//前指后
    }
  • 修改

    思路和原来的单向链表一样

  • 删除

    1. 因为是双向链表,因此,我们可以实现自我删除某个节点
    2. 直接找到要删除的这个节点,比如 temp
    3. temp.pre.next = temp.next
    4. 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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
package com.ysy.linkedlist;

public class DoubleLinkedListDemo {
public static void main(String[] args) {
//双向链表的测试
System.out.println("双向链表的测试");

//创建节点
HeroNode2 heroNode1 = new HeroNode2(1, "宋江", "及时雨");
HeroNode2 heroNode2 = new HeroNode2(2, "卢俊义", "玉麒麟");
HeroNode2 heroNode3 = new HeroNode2(3, "吴用", "智多星");
HeroNode2 heroNode4 = new HeroNode2(4, "林冲", "豹子头");

//创建双向链表
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
// //测试添加(无序)
// doubleLinkedList.add(heroNode1);
// doubleLinkedList.add(heroNode2);
// doubleLinkedList.add(heroNode3);
// doubleLinkedList.add(heroNode4);
//测试添加(有序)
doubleLinkedList.addByOrder(heroNode1);
doubleLinkedList.addByOrder(heroNode4);
doubleLinkedList.addByOrder(heroNode2);
doubleLinkedList.addByOrder(heroNode3);


//输出原链表
System.out.println("原双向链表~");
doubleLinkedList.list();

//测试更新
HeroNode2 newHeroNode = new HeroNode2(2, "小卢", "玉麒麟");
doubleLinkedList.update(newHeroNode);
System.out.println("更新后的链表~");
doubleLinkedList.list();

//测试删除
doubleLinkedList.del(2);
System.out.println("删除后的链表");
doubleLinkedList.list();
}
}

//创建一个双向链表类
class DoubleLinkedList{
//初始化一个头节点,不要动,不存放具体的数据
private HeroNode2 head = new HeroNode2(0, "", "");

public HeroNode2 getHead() {
return head;
}

//添加节点到单向列表中
//思路:不考虑编号顺序
//1.找到当前链表的最后节点
//2.将最后节点的next指向新的节点
public void add(HeroNode2 heroNode){

//因为head绩点不能动,所以需要一个辅助遍历temp
HeroNode2 temp = head;
//遍历链表,找到最后
while(true){
//找到链表的最后
if(temp.next == null){
break;
}
//如果没有找到最后,将temp后移
temp = temp.next;
}
//出循环代表temp已指向了链表的最后
//将最后这个节点指向新节点
temp.next = heroNode;
heroNode.pre = temp;
}

//根据排名添加节点到链表中,如果排名存在,则输出添加失败
public void addByOrder(HeroNode2 heroNode){
//头节点不能动,使用辅助变量temp来寻找添加的位置
//temp是位于添加位置的前一个节点,否则插入不了
HeroNode2 temp = head;
boolean flag = false;//标志添加的编号是否存在
while (true){
//链表已到最后
if(temp.next == null){
break;
}
if(temp.next.number > heroNode.number){//位置找到,插在后面
break;
}else if(temp.next.number == heroNode.number){
flag = true;//说明标号存在
break;
}
temp = temp.next;//后移,遍历链表
}
if(flag){
System.out.printf("编号 %d 已存在,不能添加\n", heroNode.number);
}else {
//插入链表中,temp后面
if (temp.next != null){
heroNode.next = temp.next;
temp.next.pre = heroNode;
temp.next = heroNode;
heroNode.pre = temp;
}else {
temp.next = heroNode;//后指前
heroNode.pre = temp;//前指后
}
}
}

//根据number来修改节点信息,number不能改
public void update(HeroNode2 newHeroNode){
//判断是否为空
if(head.next == null){
System.out.println("链表为空~");
return;
}

//找到需要修改的节点,根据number编号
//辅助变量,遍历链表
HeroNode2 temp = head.next;
boolean flag = false;
while (true){
if(temp == null){
break;//链表遍历完成
}
if(temp.number == newHeroNode.number){
//找到
flag = true;
break;
}
temp = temp.next;
}
//根据flag判断是否找到要修改的节点
if(flag){
temp.name = newHeroNode.name;
temp.nickName = newHeroNode.nickName;
}else {
System.out.printf("没有找到编号 %d 的节点\n", newHeroNode.number);
}
}

//删除节点
//思路
//1.对于双向链表,可以直接找到要删除的节点
//2.找到后,自我删除
public void del(int number){
if(head.next == null){
System.out.println("链表为空,无法删除");
return;
}
HeroNode2 temp = head.next;
boolean flag = false;//标志是否找到要删除的节点
while (true){
if(temp == null){//链表已到最后
break;
}
if(temp.number == number){
//找到
flag = true;
break;
}
temp = temp.next;//temp后移,遍历链表
}
//判断flag
if(flag){
temp.pre.next = temp.next;
//删除的节点是最后一个节点时有问题
//如果删除的是最后一个节点,就不需要执行这行,否则会出现空指针 temp.next.pre = temp.pre
if(temp.next != null){
temp.next.pre = temp.pre;
}
}else {
System.out.printf("要删除的 %d 节点不存在\n", number);
}
}


//显示链表,遍历
public void list(){
//判断链表是否为空
if(head.next == null){
System.out.println("链表为空");
return;
}

//因为头节点不能动,所以辅助变量temp遍历
HeroNode2 temp = head.next;
while (true){
//判断链表是否走到最后
if(temp == null){
break;
}
//输出节点信息
System.out.println(temp);
//将temp后移
temp = temp.next;
}
}
}

//定义HeroNode,每个HeroNode对象就是一个节点
class HeroNode2{
//data域
public int number;
public String name;
public String nickName;
//next域
public HeroNode2 next;
//pre域
public HeroNode2 pre;

//构造器
public HeroNode2(int number, String name, String nickName) {
this.number = number;
this.name = name;
this.nickName = nickName;
}

/**
* 为了显示方法,重回写toString方法
* @return
*/
@Override
public String toString() {
return "HeroNode{" +
"number=" + number +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}

文章作者: Yang Shiyu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Yang Shiyu !
  目录