循环双向链表
前情提要:
通过上文,了解相信大家已经简单了解了单链表的结构,本文主要介绍循环双向链表,简单说就是双向链表和循环链表的结合,为什么把它放在一起实现呢,因为总体而言,循环双向链表基本解决了各类链表的弊端,实用性较强。
图解:
下图即是循环双向链表,从图中可以看出,它与单链表的区别,每个结点多了一个指针,指向了前驱结点,也就是上一个结点,而另一个指针则指向了后继结点,即下一个结点,并且头结点与尾结点互连形成循环
名词:
名词 | 概念 |
---|---|
前驱结点 | 当前结点prev指针指向的结点 |
后继结点 | 当前结点next指针指向的结点 |
在往下讲之前,大家可以考虑下这麽做,可以解决单链表的哪些弊端呢?知道解决的弊端你也便很快可以理解循环链表出现的意义,想好了回答我的下述问题
1.单链表的头结点如果要访问尾结点要怎么做,时间复杂度是什么?
2.单链表要想找当前结点的上一个结点,需要怎么做,时间复杂度是什么?
3.单链表如果想删除一个元素,需要怎么做,时间复杂度是什么?
咚咚咚,在回答了我的三连问以后,大家可以来看这里啦!
循环双向链表特点:
1.通过当前结点直接获取上一结点
2.通过头结点的上一结点直接可以去找到尾结点
3.可以进行反向循环链表,即反转链表
这三个特点好好理解呦,几乎所有的链表面试题都是在潜移默化的考这些哦,不要转变一个问题,大家就不知道在问什么,学习方法很重要
Python中循环双向链表的实现
#创建结点类,增加一个prev,用来指向当前结点的前一个结点
class Node(object):
def __init__(self, value=None, prev=None, next=None):
self.value, self.prev, self.next = value, prev, next
# 循环双向链表:增删改查
class CyclicBothWayLink(object):
#初始化变量,需要在开始时便创建一个root的闭环连接,即root结点自指操作
def __init__(self):
self.root = Node()
self.length = 0
self.root.next = self.root
self.root.prev = self.root
#返回头结点的下一个结点,即开始结点
def head_node(self):
return self.root.next
#返回头结点的上一个结点,即尾结点
def final_node(self):
return self.root.prev
# 尾插
# 判断是否是头结点闭环,不是则进行尾部插入,
# 对于新结点,找到它的prev和next都指向谁
# 对于root结点,找到它的prev指向谁
# 对于final_node结点,找到它的next指向谁
# 以上三个结点的指向都有需要更新的地方,建议不熟悉的同学可以先画图,在来写码
def append(self, value):
node = Node(value)
if self.final_node() is self.root:
self.root.next = node
node.prev = self.root
else:
self.final_node().next = node
node.prev = self.final_node()
node.next = self.root # 尾部插入,新结点的next一定是root
self.root.prev = node # 尾部插入,root结点的prev一定是新结点
self.length += 1
#头插
# 与大概相同,但是新结点,root结点,final_node结点之间的关系,哪个要更新哪个不更新,要分清楚,有些不同哦
def append_left(self, value):
node = Node(value)
if self.head_node() is self.root:
node.next = self.root
self.root.prev = node
else:
cur_node = self.head_node()
node.next = cur_node
cur_node.prev = node
self.root.next = node # 头部插入,root结点的下一结点一定是新结点
node.prev = self.root # 头部插入,新结点的上一结点一定是root结点
self.length += 1
#返回链表长度
def len(self):
return self.length
#迭代结点,返回当前迭代结点
def iter(self):
if self.root.next is self.root:
raise Exception("Link is Null")
else:
cur_node = self.root.next
while cur_node is not self.final_node():
yield cur_node
cur_node = cur_node.next
yield cur_node
#返回正序的链表生成式
def iter_link(self):
return [node.value for node in self.iter()]
# 双向循环链表,可以反向遍历链表,即实现经典的空间换时间的做法,由next结点得到prev结点的操作
def reverse_iter(self):
if self.root.prev is self.root:
raise Exception("Link is Null")
else:
cur_node = self.root.prev
while cur_node is not self.head_node():
yield cur_node
cur_node = cur_node.prev
yield cur_node
#返回倒序的链表生成式
def reverse_iter_link(self):
return [node.value for node in self.reverse_iter()]
# 双向循环链表,传入node结点,而不是value,可以将delete的时间复杂度降为O(1)
def delete(self, node):
if node is self.root or node ==-1:
return
else:
node.prev.next, node.next.prev = node.next, node.prev
self.length -= 1
#find 链表,返回结点,没有返回-1
def find(self, value):
for node in self.iter():
if node.value == value:
return node
return - 1
python编写循环双向链表其实与单链表的区别无非就是多了一个prev结点,每个结点多占用一个空间,是经典的空间换时间操作,其实如果大家真正了解了链表的话,循环双向链表可以很快写出来,如果在看到这里的时候有的同学还是不能不看提示便写出整个链表代码的思路,建议可以先理解一遍概念,在不看任何提示的情况下重新写一遍单链表,大家一定要去实践,代码都知道空间换时间来提速,难道我们的学习,在没有实操的情况下可以真正去应用链表嘛,急于求成无异于掩耳盗铃,拔苗助长,所以,加油吧!
完成链表代码的同学,给大家拓展一个需求:控制循环链表的内存占用空间不超过100个字节
注:欢迎大家沟通学习,可以但不限于Web,数据结构,操作系统,数据库,中间件等知识