(Python3)数据结构--双向链表的原理及实现

Winona ·
更新时间:2024-11-10
· 956 次阅读

前言 有Python基础 学过数据结构那自然是最好的 原理和需要注意的点 双向链表

在这里插入图片描述

双向链表和单链表的差别在哪?双向链表的节点和单链表的节点是不一样的。单链表的节点有item和next,而双向链表的节点还必须有一个指向前一个节点的prior(别的命名也行,反正必须有个东西指向前一个节点)。prior存在的意义也是为了更好地查找节点的前驱节点。因此必须对Node进行改造。 实现 节点的定义 class Node(object): # 初始化时它就只是个孤家寡人,前不着村后不着店,prior和next都是空 def __init__(self, item): self.item = item self.prior = None self.next = None 初始化:双向链表的构造函数和单链表一样,只需要设置空的头节点即可。 class DeLink(object): def __init__(self): self.__head = None 事实上,双向链表比单链表的节点多了一个属性prior,其他的方法照样可以沿用。参照博客 https://blog.csdn.net/sf9898/article/details/104946291 将单链表的代码拷贝下来,在单链表的class Node下按照上面说的那样子改造,加一个prior属性,其他的别改,直接运行并没有毛病。其原因在于增加了一个并没有使用到的prior属性,运行的实际上还是单链表。 正如前面讲的,prior存在的意义是为了查找前一个节点,这也是双向链表的优点。那么就可以有利用prior属性查找节点的方法。 接下来按照双向链表的原理重新构造一下class DeLink size 方法不需要讲究,利用原来的单链表方法即可,毕竟只是求个数。当然,判空和遍历也可以沿用。 def isEmpty(self): return self.__head is None def size(self): count = 0 cur = self.__head while cur: count += 1 cur = cur.next return count def travel(self): cur = self.__head while cur: print(cur.item, end=' ') cur = cur.next print('') 查找,删除数值等函数亦是可以沿用,加上了prior属性之后变化的主要是插入和删除,其他的沿用即可。接下来重点看看插入和删除。 头部插入

在这里插入图片描述

# 头部插入 def addFront(self, item): node = Node(item) # 新的节点只有item,next和prior都是None node.next = self.__head # 新的节点的next指向当前的第一个节点 if self.__head: self.__head.prior = node # 不为空时设置当前第一个节点的prior指向新来的node self.__head = node # 新来的node成为新的头结点 尾部插入:尾部插入需要将原来的尾部节点last的next指向新的node(原来的last的next是指向None的),node的prior指向last(这一步要有,这样通过node的prior就可以找到last了)。为了减少工作量,直接在原有的单链表上面改了。

在这里插入图片描述

# 尾部插入 def addBack(self, item): node = Node(item) if self.__head is None: self.__head = node return cur = self.__head pre = None while cur: pre = cur cur = cur.next # 当cur 为最后一个节点时带入,pre更新为最后一个节点,cur更新为最后一个节点的下一个节点即为空, # 下一次while cur 时会退出循环,此时的pre表示的就是最后一个节点,将node挂到pre的后面即可 pre.next = node # 这里的pre相当于例子中的last # 仅加了下面这句 node.prior = pre 中间插入:需要注意的是要找到插入位置的前一个节点。如图,找到pre,之后: pre.next = node node.prior = pre node.next = nd nd.prior = node

在这里插入图片描述

# 插入节点, 节点在插入后是第 pos 个节点,当然这个函数也可以实现头部插入和尾部插入的功能 # 故pos取值1时,即头部插入,取值size+1时是尾部插入。因此取值的合法范围是[1,size + 1] def insert(self, pos, item): if pos > (self.size() + 1) or pos < 1: return if pos == 1: self.addFront(item) return node = Node(item) cur = self.__head pre = None for i in range(pos - 1): pre = cur cur = cur.next # 1) pre.next = node # 2) node.prior = pre # 3) node.next = nd # 4) nd.prior = node pre.next = node node.prior = pre node.next = cur if cur: cur.prior = node 头部删除:更新头结点为头结点的下一个节点,原头结点的next指向None,现头结点的prior指向None。

在这里插入图片描述

# 删除头部结点 def removeFront(self): cur = self.__head self.__head = self.__head.next cur.next = None self.__head.prior = None 删除尾部结点

在这里插入图片描述

# 删除尾部节点 def removeBack(self): # 空节点时 if self.__head is None: return # 只有一个节点 if self.__head and self.__head.next is None: self.__head = None return # 链表节点有两个及以上 cur = self.__head # 当前节点 pre = None # 前一个节点 cn = cur.next # 后一个节点 # 刚开始cur取到的是第一个节点,cn是第二个 while cn: pre = cur cur = cur.next cn = cur.next # 仅修改此处 pre.next = None cur.prior = None 中间删除

在这里插入图片描述

# 删除指定数值的节点 def delete(self, item): if self.__head is None: return if self.__head.item == item: self.__head = None return cur = self.__head.next # 取第二个节点 pre = self.__head # 第一个节点 while cur: if cur.item == item: # 仅修改此处 pre.next = cur.next cur.prior = None cur.next.prior = pre cur.next = None break else: pre = cur cur = cur.next 参照之前单链表进行测试,下面是完整代码,如有错误之处还望批评指出。 class Node(object): # 初始化时它就只是个孤家寡人,前不着村后不着店,prior和next都是空 def __init__(self, item): self.item = item self.prior = None self.next = None class DeLink(object): def __init__(self): self.__head = None def isEmpty(self): return self.__head is None # 头部插入 def addFront(self, item): node = Node(item) # 新的节点只有item,next和prior都是None node.next = self.__head # 新的节点的next指向当前的第一个节点 if self.__head: self.__head.prior = node # 设置当前第一个节点的prior指向新来的node self.__head = node # 新来的node成为新的头结点 # 尾部插入 def addBack(self, item): node = Node(item) if self.__head is None: self.__head = node return cur = self.__head pre = None while cur: pre = cur cur = cur.next # 当cur 为最后一个节点时带入,pre更新为最后一个节点,cur更新为最后一个节点的下一个节点即为空, # 下一次while cur 时会退出循环,此时的pre表示的就是最后一个节点,将node挂到pre的后面即可 pre.next = node # 这里的pre相当于例子中的last node.prior = pre def size(self): count = 0 cur = self.__head while cur: count += 1 cur = cur.next return count def travel(self): cur = self.__head while cur: print(cur.item, end=' ') cur = cur.next print('') # 删除头部节点 def removeFront(self): cur = self.__head self.__head = self.__head.next cur.next = None self.__head.prior = None # 删除尾部节点 def removeBack(self): # 空节点时 if self.__head is None: return # 只有一个节点 if self.__head and self.__head.next is None: self.__head = None return # 链表节点有两个及以上 cur = self.__head # 当前节点 pre = None # 前一个节点 cn = cur.next # 后一个节点 # 刚开始cur取到的是第一个节点,cn是第二个 while cn: pre = cur cur = cur.next cn = cur.next # 仅修改此处 pre.next = None cur.prior = None # 查找链表中有没有item,有返回True,没有则返回False def search(self, item): cur = self.__head res = False while cur: if cur.item == item: res = True break else: cur = cur.next return res # 查找某个数的下标 # def searchIndex(self, item): # if self.search(item): # # 存在的话才进行 # cur = self.__head # index = -1 # while cur: # index += 1 # if cur.item == item: # break # else: # cur = cur.next # return index # else: # return -1 # 删除指定数值的节点 def delete(self, item): if self.__head is None: return if self.__head.item == item: self.__head = None return cur = self.__head.next # 取第二个节点 pre = self.__head # 第一个节点 while cur: if cur.item == item: pre.next = cur.next cur.prior = None cur.next.prior = pre cur.next = None break else: pre = cur cur = cur.next # 插入节点, 节点在插入后是第 pos 个节点,当然这个函数也可以实现头部插入和尾部插入的功能 # 故pos取值1时,即头部插入,取值size+1时是尾部插入。因此取值的合法范围是[1,size + 1] def insert(self, pos, item): if pos > (self.size() + 1) or pos < 1: return if pos == 1: self.addFront(item) return node = Node(item) cur = self.__head pre = None for i in range(pos - 1): pre = cur cur = cur.next # 1) pre.next = node # 2) node.prior = pre # 3) node.next = nd # 4) nd.prior = node pre.next = node node.prior = pre node.next = cur if cur: cur.prior = node ll = DeLink() print(ll.isEmpty()) # 初始化时为空,理应输出True for i in range(5): ll.addFront(i) print('size:', ll.size()) # 5个数 ll.travel() # 4 3 2 1 0 for i in range(5): ll.addBack(i) ll.travel() # 4 3 2 1 0 0 1 2 3 4 ll.removeFront() ll.travel() # 3 2 1 0 0 1 2 3 4 print('----') ll.removeBack() ll.travel() # 3 2 1 0 0 1 2 3 ll.insert(9, 12) ll.travel() # 3 2 1 0 0 1 2 3 12 print(ll.search(13)) # 没有,False print(ll.search(1)) # True ll.delete(2) ll.travel() # 3 1 0 0 1 2 3 12 print('------') # print(ll.searchIndex(12)) ll.insert(8, 100) ll.travel() # 3 1 0 0 1 2 3 100 12 结果

在这里插入图片描述

可以发现写起来代码量会多一点点,事实上,很多东西用单链表就能解决了,双向链表比单链表多了一个prior,这一点是双向链表的优点,同时也是它的缺点,毕竟多一个元素就多占一点空间,因此还是单链表用的比较多。
作者:1、、



链表 数据结构 Python3 数据 Python 双向链表

需要 登录 后方可回复, 如果你还没有账号请 注册新账号