之前写的单向链表和环形链表都只是单向的,只能单向遍历,不能根据后面的节点获取前面的节点,除非进行反转操作。
双向链表每个节点都有两个指针,这两个指针分别指向前后两个节点,这样就可以从任意一个节点从两个方向获取其他的所有节点,非常方便。但是由于每个节点有两个指针,所以双向链表比较消耗空间。
在设计双向链表时,通常会加上一个链表头指针,该链表头指针的数据字段不存放任何数据。
双向链表的可以是环形的,也可以不是环形的,如果是环形的话,那么最后一个节点的一个指针将指向链表头,链表头的一个指针将指向最后一个节点;如果不是环形的话,那么最后一个节点的一个指针和链表头的一个指针都将指向None。
我在这里实现的是一个环形的双向链表,这样我就可以从链表头开始,从两个方向中任意选择一个方向来进行操作。
我在这里主要实现了环形双向链表的:双向新增,双向遍历,双向插入,双向删除。
如图为双向环形链表示意图,每一个节点都被两个指针所指向,同时每个节点也指向了两个节点。
实现代码如下:
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
|
class Player: """节点类""" def __init__( self ): """初始化姓名,分数,指针""" self .name = '' self .score = 0 self .rlink = None self .llink = None def ergodic(head, num = None , is_print = False , left = False ): """遍历函数,num是遍历到哪一个位置序号,is_print是否触发打印方法,left表示是否由head开始往左遍历""" ptr = head count = 0 while True : if num = = count: break if not left: if ptr.rlink ! = head: ptr = ptr.rlink else : break else : if ptr.llink ! = head: ptr = ptr.llink else : break count + = 1 if is_print: print ( 'No.' + str (count), ptr.llink.name if ptr.llink ! = head else 'head' , '<---' , ptr.name, ptr.score, '--->' , ptr.rlink.name if ptr.rlink ! = head else 'head' ) return ptr # 返回遍历完成后的最后一个节点 head = Player() # 初始化一个链表头指针,不用来存放任何数据 head.rlink = head # 初始化右指针 head.llink = head # 初始化左指针 while True : select = input ( "(1).新增 (2).查看 (3).插入 (4).删除 (5).离开\n输入:" ) if select = = "1" : # 新增节点,分为右新增和左新增 direction = input ( "(1).右新增 (2).左新增\n输入:" ) if direction not in ( "1" , "2" ): print ( "输入错误" ) continue new_data = Player() new_data.name = input ( "姓名:" ) new_data.score = input ( "分数:" ) if direction = = "1" : # 右新增 ptr = ergodic(head) # 从head开始向右遍历获取最后一个节点 ptr.rlink = new_data new_data.llink = ptr new_data.rlink = head head.llink = new_data else : # 左新增 ptr = ergodic(head, left = True ) # 从head开始向左遍历获取最后一个节点 ptr.llink = new_data new_data.rlink = ptr new_data.llink = head head.rlink = new_data elif select = = "2" : # 遍历查看所有节点,分为右遍历和左遍历 direction = input ( "(1).右遍历 (2).左遍历\n输入:" ) if direction = = "1" : # 右遍历 ergodic(head, is_print = True ) elif direction = = "2" : # 左遍历 ergodic(head, is_print = True , left = True ) else : print ( "输入错误" ) elif select = = '3' : # 插入节点,分为右插入和左插入 direction = input ( "(1).右插入 (2).左插入\n输入:" ) if direction not in ( "1" , "2" ): print ( "输入错误" ) continue try : num = int ( input ( "请输入需要插入的节点位置序号:" )) # 输入序号必须是大于0的正整数,如果输入大于最后一个节点的序号则插入到最后一个节点之后 if num < 1 : print ( "输入必须为大于0的正整数" ) continue except ValueError: print ( "输入有误" ) continue insert_data = Player() insert_data.name = input ( "姓名:" ) insert_data.score = input ( "分数:" ) if direction = = "1" : # 右插入 ptr = ergodic(head, num - 1 ) # 获取需要插入位置的前一个节点,新插入的节点就在这个节点的后面 insert_data.llink = ptr insert_data.rlink = ptr.rlink ptr.rlink = insert_data insert_data.rlink.llink = insert_data else : # 左插入 ptr = ergodic(head, num - 1 , left = True ) insert_data.rlink = ptr insert_data.llink = ptr.llink ptr.llink = insert_data insert_data.llink.rlink = insert_data elif select = = '4' : # 删除节点,分为右删除和左删除 direction = input ( "(1).右删除 (2).左删除\n输入:" ) if direction not in ( "1" , "2" ): print ( "输入错误" ) continue try : num = int ( input ( "请输入需要删除的节点位置序号:" )) # 输入序号必须是大于0的正整数,如果输入大于最后一个节点的序号则删除最后一个节点 if num < 1 : print ( "输入必须为大于0的正整数" ) continue except ValueError: print ( "输入有误" ) continue if direction = = "1" : # 右删除 ptr = ergodic(head, num) # 获取需要删除的节点 else : # 左删除 ptr = ergodic(head, num, left = True ) ptr.llink.rlink = ptr.rlink ptr.rlink.llink = ptr.llink elif select = = '5' : print ( "成功离开" ) break else : print ( "输入错误,请重试" ) |
部分运行效果如下:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/heibuliuqiu_gk/article/details/102687261