双向链表

一:为什么要使用双向链表?

  1. 单向链表,查找的方向只能是一个方向,二双向链表可以向前或向后查询。
  2. 单向链表不能自我删除,需要靠辅助节点,而双向链表,则可以自我删除,所以我们单链表删除节点时,总是找到temp,temp是待删除节点的前一个节点。

二:分析双向链表的增、删、改、查、遍历的功能!

1573980862995

三:代码实现

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace demo4
{
public class DoubleLinkedList
{
//初始化一个头结点,头结点不要动,不存放具体的数据
private HeroNode head = new HeroNode(0, "", "");

//返回头结点
public HeroNode gethead()
{
return head;
}
//显示链表
public void showLink()
{
if (head.next == null)
{
Console.WriteLine("链表为空!");
}
//因为头结点不能懂,我们需要一个辅助变量来遍历
HeroNode temp = head.next;
while (true)
{
//找到链表的最后
if (temp.next == null)
{
Console.WriteLine(temp);
break;
}
Console.WriteLine(temp);
//将temp后移
temp = temp.next;
}
}
//添加节点到单向链表
public void add(HeroNode heroNode)
{
//因为head节点不能动,因此我们需要一个辅助遍历temp
HeroNode temp = head;
//遍历链表,找到最后
while (true)
{
//找到链表的最后
if (temp.next == null)
{
break;
}
//如果没有找到最后
temp = temp.next;
}
//当退出while循环时,temp就指向了链表的最后
temp.next = heroNode;
heroNode.pre = temp;
}
//按照编号添加
public void addByOrder(HeroNode heroNode)
{
HeroNode temp = head;
bool flag = false;//标志添加的编号是否存在,默认为false
while (true)
{
if (temp.next == null)//说明temp已经在链表的最后
{
break;
}
if (temp.next.no > heroNode.no)//位置找到,就在temp后面插入
{
break;

}
else if (temp.next.no == heroNode.no)
{
flag = true;//说明编号存在
break;
}
temp = temp.next;
}
//判断flag的值,如果flag==true,说明编号存在
if (flag)
{
Console.WriteLine("准备插入的英雄的编号{0}已经存在了,不能加入\n", heroNode.no);
}
else
{
//插入到temp后面
temp.next.pre = heroNode;
heroNode.next = temp.next;

temp.next = heroNode;
heroNode.pre = temp;
}
}
//修改结点的信息,根据编号no来修改,即no编号不能改
public void update(HeroNode newheroNode)
{
if (head.next == null)
{
Console.WriteLine("链表为空!");
}
else
{
HeroNode temp = head;
bool flag = false;//是否找到该结点
while (true)
{
if (temp == null)
{
break;
}
if (temp.no == newheroNode.no)
{
//找到
flag = true;
break;
}
temp = temp.next;
}
if (flag)
{
temp.name = newheroNode.name;
temp.nickname = newheroNode.nickname;
}
else
{
Console.WriteLine("没有找到编号{0}的结点,不能修改\n", newheroNode.no);
}
}
}
//删除结点
public void delete(int n)
{
if (head.next == null)
{
Console.WriteLine("链表为空!");
}
else
{
HeroNode temp = head;
bool flag = false;//是否找到该结点
while (true)
{
if (temp == null)
{
break;
}
if (temp.no == n)
{
//找到
flag = true;
break;
}
temp = temp.next;
}
if (flag)
{
temp.pre.next = temp.next;
if (temp.next!=null)
{
temp.next.pre = temp.pre;

}

}
else
{
Console.WriteLine("没有找到编号{0}的结点,不能修改\n", n);
}
}
}
}

public class HeroNode
{
public int no;
public string name;
public string nickname;
public HeroNode next;//指向下一个节点
public HeroNode pre;//指向下一个节点
//构造器
public HeroNode(int no, string name, string nickname)
{
this.no = no;
this.name = name;
this.nickname = nickname;
}
//为了现实方法,我们重新toString
public override string ToString()
{
return "HeroNode[no=" + no + ",name=" + name + ",nickname=" + nickname + "]";
}
}
}

四:代码地址

GitHub源码地址