Python实现双向链表

关于链表的介绍,请参考:https://blog.csdn.net/weixin_43790276/article/details/104033254

本篇文章使用 Python 来实现双向链表。

一、定义一个创建节点的类

链表是由一个一个的节点组成的,在创建链表之前,要先创建节点,然后把节点“串”到链表上。在同一个链表中,每个节点的结构都相同,只是节点中保存的数据不同和链接域的值不同,所以提前声明一个创建节点的类,需要创建节点时实例化即可。

# coding=utf-8
class Node(object):def __init__(self, data):self.prev = Noneself.data = dataself.next = None

双向链表的节点包含三个域,一个信息域(元素域)和两个链接域(引用域),一个链接域指向前一个节点,一个链接域指向后一个节点,头节点向前的链接域指向空,尾节点向后的链接域指向空。在实例化一个节点时,传入该节点中保存的数据,保存到信息域中,链接域默认为空,当对节点进行“链接”操作时,再设置具体的链接域。

二、定义一个双向链表类

对于链表,在没有将节点“链接”上去时,链表里没有节点和数据。实例化一个双向链表时,这个双向链表是一个空链表,把节点依次“链接”上去后,链表中才有节点和数据。

在链表中,要找到链表的某个节点,需要从链表的头节点开始,依次寻找,所以在实例化一个链表时,必须定义好链表的“头”,当加入头节点时,将链表的“头”指向头节点。

定义一个双向链表类 DoubleLinkList,初始化一个双向链表时,链表的“头”指向空值,默认为空链表。

class DoubleLinkList(object):def __init__(self):self.__head = None

三、实现双向链表的展示功能

    def is_empty(self):return not self.__headdef show(self):if self.is_empty():print('空链表')returncur = self.__headwhile cur is not None:if cur.next is not None:print(cur.data, end='←→')else:print(cur.data)cur = cur.next

先实现判断链表是否为空的方法 is_empty() ,实例化链表时,默认是空的,链表的头指向为空。所以,如果链表的头指向为空(对应布尔值False), is_empty() 的值就为 True ,反之。

展示链表中的数据,就是将链表中所有的数据依次打印输出。链表不像顺序表有“索引”,链表只能从头节点开始依次往下找,直到尾节点。所以链表不能使用 for 循环进行遍历,只能使用 while 循环进行遍历,并使用一个游标 cur 来记录当前所处的节点,通过游标 cur 向下一个节点移动来遍历,当向后的链接域指向空(尾节点)时停止。

实现 show() 方法时,为了更形象地展示链表中每个节点的关系,我在相邻两个节点之间使用左箭头加右箭头连接(空链表无效果)。

if __name__ == '__main__':d = DoubleLinkList()print("is_empty: ", d.is_empty())d.show()

运行结果:

is_empty:  True
空链表

四、实现双向链表中添加数据的功能

    def add(self, data):node = Node(data)if self.is_empty():self.__head = nodereturnnode.next = self.__headself.__head.prev = nodeself.__head = nodedef append(self, data):if self.is_empty():self.add(data)returncur = self.__headwhile cur.next is not None:cur = cur.nextnode = Node(data)node.prev = curcur.next = nodedef length(self):length = 0cur = self.__headwhile cur is not None:length += 1cur = cur.nextreturn lengthdef insert(self, index, data):if index <= 0:self.add(data)returnif index > self.length() - 1:self.append(data)returncur = self.__headfor i in range(index - 1):cur = cur.nextnode = Node(data)node.next = cur.nextnode.prev = curcur.next.prev = nodecur.next = node

添加数据到链表中,可以从头部添加、从尾部添加或从指定位置添加。

无论将数据添加到链表的哪个位置,都要先创建一个新节点,新节点里存放对应的数据,然后将新节点添加到指定的位置。

add(data):从头部添加时,链表原来的头节点会成为第二个节点,新节点成为头节点。添加分为三步,第一步将新节点向后的链接域指向原来的头节点,第二步将旧的头节点向前的链接域指向新节点,第三步将链表的头指向新节点(注意顺序不能变)。如果原来的链表为空,则链表的头原来是指向空,所以直接将链表的头指向新节点即可。

append(data):从尾部添加时,找到链表的尾节点,添加分为两步,第一步将尾节点向后的链接域指向新节点,第二步将新节点向前的链接域指向尾节点。如果原来的链表为空,则链表没有尾节点,这时候与从头部添加一样,直接调用即可。

insert(index, data):在指定位置添加数据时,要使用一个游标 cur 来找到此位置的前一个节点,添加分为四步,第一步将新节点向后的链接域指向此位置原来的节点,第二步将游标记录的节点向后的链接域指向新节点,第三步将此位置原来的节点向前的链接域指向新节点,第四步将新节点向前的链接域指向游标记录的节点,这样就成功将新节点插入到了指定位置。

如果指定的位置是负数或超过了链表最大长度,则需要特殊处理,上面的处理是负数在头部添加,超过最大长度在尾部添加。也可以直接抛出 IndexError ,这个可以自己按需选择。

同时,上面实现了获取双向链表长度的方法 length(),返回链表当前的节点个数。

    d.add(10)d.add(100)d.append(20)d.append(30)d.append(40)d.show()d.insert(1, 200)d.show()print("链表长度:", d.length())

运行结果:

100←→10←→20←→30←→40
100←→200←→10←→20←→30←→40
链表长度: 6

五、实现双向链表的查询和修改功能

    def is_exist(self, value):cur = self.__headwhile cur is not None:if cur.data == value:return Truecur = cur.nextreturn Falsedef index(self, value):index = 0cur = self.__headwhile cur is not None:if cur.data == value:return indexcur = cur.nextindex += 1return -1def setitem(self, index, value):if index < 0:raise IndexErrorif index > self.length() - 1:raise IndexErrorcur = self.__headfor i in range(index):cur = cur.nextcur.data = value

is_exist(value):判断一个数据是否存在链表中,遍历双向链表的每个节点,如果节点的数据值与目标值相等,则说明链表中存在目标值。

index(value):返回一个数据在链表中的第几个节点,与判断是否存在的实现方式一样,这里返回的是数据处于第几个节点中,如果链表中不存在这个数据,则返回-1。

setitem(index, value):修改指定位置的节点的数据,先根据给定的值,找到链表中该位置的节点,然后修改节点中的数据。如果数值小于零或大于链表长度,抛出 IndexError 。

    print(d.is_exist(200))print(d.index(20))d.setitem(2, 300)d.show()

运行结果:

True
3
100←→200←→300←→20←→30←→40

六、实现双向链表的删除功能

    def remove(self, index):if index < 0:raise IndexErrorif index > self.length() - 1:raise IndexErrorcur = self.__headfor i in range(index):cur = cur.nextif cur == self.__head:self.__head = self.__head.nextif cur.next:cur.next.prev = Nonereturnif cur.next is None:cur.prev.next = cur.nextreturncur.prev.next = cur.nextcur.next.prev = cur.prevdef delete(self, value):cur = self.__headwhile cur is not None:if cur.data == value:if cur == self.__head:self.__head = self.__head.nextif cur.next:cur.next.prev = Nonereturnif cur.next is None:cur.prev.next = cur.nextreturncur.prev.next = cur.nextcur.next.prev = cur.prevreturncur = cur.nextdef delete_all(self, value):cur = self.__headwhile cur is not None:if cur.data == value:if cur == self.__head:self.__head = self.__head.nextif cur.next:cur.next.prev = Noneself.delete_all(value)returnif cur.next is None:cur.prev.next = cur.nextself.delete_all(value)returncur.prev.next = cur.nextcur.next.prev = cur.prevself.delete_all(value)returncur = cur.next

remove(index):删除指定位置的节点,将节点删除后,要保证链表不断开。通过游标 cur 找到节点,再使用一个游标 prev 来记录当前节点的前一个节点,删除分为两步,第一步将前一个节点向后的链接域指向当前节点的后一个节点,第二步将后一个节点向前的链接域指向前一个节点。如果删除的是头节点,则将链表的头指向第二个节点,然后将第二个节点向前的链接域指向空,如果只有一个节点,则直接将链表的头指向空就行了。如果删除的是尾节点,则将倒数第二个节点向后的链接域指向空。如果指定的位置小于零或超过链表长度,则抛出 IndexError 。

delete(value):删除指定值的节点,先遍历链表,找到对应值的节点。使用游标 cur 记录要删除的节点,使用另一个游标 prev 来记录当前节点的前一个节点,删除步骤与 remove(index) 相同。

使用这个方法,如果链表中有多个满足条件的节点,只会删除最前面的一个节点。

delete_all(value):删除数据等于指定值的所有节点,如果链表中有多个节点的数据与目标值相等,删除第一个节点后,链表的长度发生了改变,继续遍历和删除节点,会出现删除不完全甚至程序出错的情况。所以在删除第一个节点之后,递归调用自身,这样重新遍历时使用的是新的链表长度,不会出现漏删或错误。

    d.remove(3)d.show()d.delete(40)d.show()d.add(40)d.insert(3, 40)d.insert(3, 40)d.append(40)d.append(40)d.show()d.delete_all(40)d.show()

运行结果:

100←→200←→300←→30←→40
100←→200←→300←→30
40←→100←→200←→40←→40←→300←→30←→40←→40
100←→200←→300←→30

以上就是用 Python 实现的双向链表及双向链表的一些简单操作方法。

Python实现双向链表相关推荐

  1. python双向索引什么意思_(转)Python 实现双向链表(图解)

    原文:https://blog.csdn.net/qq490691606/article/details/49948263 Python 实现双向链表(图解) 双向链表 双向链表也叫双链表,是链表的一 ...

  2. python程序双向链表_Python 实现双向链表(图解)

    原文:https://blog.csdn.net/qq490691606/article/details/49948263 git 路径 https://github.com/wangpanjun/d ...

  3. python数据结构与算法总结

    python常用的数据结构与算法就分享到此处,本月涉及数据结构与算法的内容有如下文章: <数据结构和算法对python意味着什么?> <顺序表数据结构在python中的应用> ...

  4. 3.Python数据结构与算法分析课后习题(第二版)__chapter3

    chpater3_answer 一.讨论题 二.编程练习 1.修改从中序到后序的转换算法,使其能处理异常情况. 2.修改计算后序表达式的算法,使其能处理异常情况. 3.结合从中序到后序的转换算法以及计 ...

  5. python中的指针_python实现指针

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 例如,您可以这样做: a = ; a has an array b = & ...

  6. python单链表类_python 链表类

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 一般我们都构造双向循环链表. 二 python单向链表实现1 单项链表实现app ...

  7. python 读取内存二叉树_二叉树类python

    python中的树数据结构 线性数据中的典型顺序表和链表已经讲完: <顺序表数据结构在python中的应用> <python实现单向链表数据结构及其基本方法> <pyth ...

  8. python单链表逆序_python链表倒序

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! python 实现双向链表(图解)----双向链表双向链表也叫双链表,是链表的一 ...

  9. python gc教程_python中的垃圾回收(GC)机制

    一.引用计数 Python 垃圾回收以引用计数为主,分代回收为辅.引用计数法的原理是每个对象维护一个ob_refcnt,用来记录对象被引用的次数,也就是用来追踪有多少个引用指向了对象,当发生以下四种情 ...

最新文章

  1. VC提前注入.net软件的方法
  2. python3中的新式类与经典类对比
  3. SpringMVC @RequestBody 接收Json数组对象
  4. JavaScript中的继承入门
  5. oracle--rowid
  6. WatchOS系统开发大全(4)-WatchApp生命周期
  7. 链表的数据域怎么使用结构体_一步一步教你从零开始写C语言链表
  8. ARP攻击网络上不去,可以进行mac地址绑定
  9. 安装nvm nodejs npm webpack vue vue-cli
  10. 傅里叶变换(时域频域)
  11. win10下pytorch 安装以及查看版本
  12. 笔记本BIOS能检测到固态,进入系统后磁盘管理不显示固态硬盘
  13. 超级账本HyperLedger的Fabric-CA的使用(两个组织一个Orderer三个Peer),带视频演示
  14. linux上电自动开启wifi脚本,archlinux 开机自动连接wifi
  15. 说话中的引题技巧,及电影刘三姐中的歌词汇总
  16. Java基础教程-刘刚-专题视频课程
  17. Genetic Fraud
  18. 【SpringBoot】SpringBoot @ConfigurationProperties 注解 用法与加载static静态属性
  19. pdf文件怎么修改文字
  20. mysql只允许指定ip网段_MySQL允许某个IP网段从远程访问的方法

热门文章

  1. 深入学习SpringMVC以及学习总结
  2. mac上配置mysql与redis server,并结合Pydev准备某爬虫环境
  3. SSM+easyUI(框架的搭建)
  4. 图解CentOS系统启动流程
  5. android中保存Bitmap图片到指定文件夹中的方法
  6. 文档转换乱码异常解决:unoconv openoffice libreoffice
  7. 内网穿透工具 Ngrok
  8. vuejs 指令封装 button 加载效果_这些Vue自定义指令,让你的项目开发爽到爆
  9. CodePush热更新组件详细接入教程
  10. leetcode 只出现一次的数字