双向链表

双向链表

双向链表

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

管理单向链表的缺点分析:
  • 单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找
  • 单向链表不能自我删除,需要靠辅助节点,而双向链表,则可以自我删除,前面单链表删除节点时,总是找到temp,temp时待删除节点的前一个节点
分析双向链表的遍历,添加,修改,删除的操作思想
  • 遍历方式和单链表一样,只是可以向前,也可以向后查找
  • 添加(默认添加到双向链表的最后)
    • 先找到双向链表的最后这个节点
    • temp.next=newHeroNode
    • newHeroNode.pre=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
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
public class DoubleLinkedListDemo {
public static void main(String[] args) {
System.out.println("双向链表的测试~~~");
HeroNode2 hero1 = new HeroNode2(1, "宋江", "及时雨");
HeroNode2 hero2 = new HeroNode2(2, "卢俊义", "玉麒麟");
HeroNode2 hero3 = new HeroNode2(3, "吴用", "智多星");
HeroNode2 hero4 = new HeroNode2(4, "林冲", "豹子头");
// 创建双向链表对象
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
// doubleLinkedList.add(hero1);
// doubleLinkedList.add(hero2);
// doubleLinkedList.add(hero3);
// doubleLinkedList.add(hero4);

doubleLinkedList.addByOrder(hero3);
doubleLinkedList.addByOrder(hero2);
doubleLinkedList.addByOrder(hero1);
doubleLinkedList.addByOrder(hero4);
System.out.println("添加链表~~~");
doubleLinkedList.list();
// 修改链表
System.out.println("修改链表~~~");
HeroNode2 newHeroNode = new HeroNode2(4, "小贾", "小贾嗯嗯");
doubleLinkedList.update(newHeroNode);
doubleLinkedList.list();
// 删除
System.out.println("删除后~~~");
doubleLinkedList.del(4);
doubleLinkedList.list();
}
}

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

public HeroNode2 getHead() {
return head;
}

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

// 添加一个节点到双向链表的最后
public void add(HeroNode2 heroNode) {
// 因为head节点不能动,因此我们需要一个辅助变量temp
HeroNode2 temp = head;
// 遍历链表,找到最后
while (true) {
// 找到链表的最后
if (temp.next == null) {
break;
}
// 如果没有找到最后,就将temp后移
temp = temp.next;
}
// 当退出while循环时,temp就指向了链表的最后
// 形成一个双向链表
temp.next = heroNode;
heroNode.pre = temp;
}

public void addByOrder(HeroNode2 heroNode) {
// 因为头节点不能动,因此我们通过一个辅助变量来帮助找到添加的位置
HeroNode2 temp = head;
boolean flag = false;// 标识添加的编号是否存在,默认为false
while (true) {
if (temp.next == null) {// 说明temp已经在链表的最后
break;
}
if (temp.next.no > heroNode.no) {// 位置找到,就在temp的后面插入
break;
} else if (temp.no == heroNode.no) {// 说明希望添加的heroNode的编号已存在
flag = true;// 说明编号存在
break;
}
temp = temp.next;// 后移,遍历当前链表
}
// 判断flag值
if (flag) {// 不能添加,说明编号存在
System.out.printf("准备插入的英雄的编号%d已经存在,不能添加\n", heroNode.no);
} else {
// 添加到链表中,temp的后面
if (temp.next != null) {
heroNode.next = temp.next;
heroNode.next.pre = heroNode;
}
temp.next = heroNode;
heroNode.pre = temp;
}
}

// 修改一个节点的内容
// 修改节点的信息,根据no编号来修改,即no编号不能改
// 1、根据newHreoNode的no来修改即可
public void update(HeroNode2 newHeroNode) {
// 判断是否为空
if (head.next == null) {
System.out.println("链表为空~~~");
return;
}
// 找到需要修改的节点,根据no编号
// 定义一个辅助变量
HeroNode2 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.printf("没有找到编号%d的节点,不能修改\n", newHeroNode.no);
}
}

// 从双向链表中删除一个节点
// 对于双向链表,可以直接找到要删除的节点
// 找到后,自我删除即可
public void del(int no) {
// 判断当前链表是否为空
if (head.next == null) {
System.out.println("链表为空,无法删除~~~");
return;
}
HeroNode2 temp = head.next;
boolean flag = false;// 标识是否找到
while (true) {
if (temp == null) {// 已经到链表最后
break;
}
if (temp.no == no) {
flag = true;// 找到了待删除节点的前一个节点temp
break;
}
temp = temp.next;
}
if (flag) {// 找到
// 可以删除
temp.pre.next = temp.next;
// 这里的代码有问题
// 如果是最后一个节点,就不需要执行下面这句代码,否则会出现空指针异常
if (temp.next != null) {
temp.next.pre = temp.pre;
}
} else {
System.out.printf("要删除的%d节点不存在", no);
}
}

}

//定义HeroNode,每个HeroNode对象就是一个节点
class HeroNode2 {
public int no;
public String name;
public String nickname;
public HeroNode2 next;// 指向下一个节点
public HeroNode2 pre;// 指向上一个节点
// 构造器

public HeroNode2(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}

// 为了显示方法,我们重写toString
@Override
public String toString() {
return "HeroNode2 [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
}
}