数据结构链表之双向链表:Python3 实现双向链表——2

Python3 实现双向链表

双向链表

  • 定义:双向链表是链表中的一种,双向链表也叫双链表,它由多个节点组成,每个节点由一个数据域和两个指针域组成,一个指针指向前驱元素,一个指向后继元素
    在这里插入图片描述
    双向链表一般用来构造循环链表,这个后面我们也会学习到

定义简单的双向链表

class Node:"""The nodes of double linked list"""def __init__(self, item):self.prev = Noneself.item = itemself.next = Noneclass DoubleLinkedList:def __init__(self):self.head = Noneif __name__ == "__main__":DLL = DoubleLinkedList()DLL.head = Node(2)DLL.head.next = Node(1)DLL.head.next.prev = DLL.headprint(DLL.head.item)# Point to the second Nodeprint(DLL.head.next.item)# Point to the headprint(DLL.head.next.prev.item)print(DLL.head.next.next)

用起来实在很复杂,举了两个例子,要写一大串
那么我们为它实现一些常用的功能:

  1. 链表长度self.len,这次使用初始化属性,根据操作的影响改变长度的方法
  2. 判断是否为空is_empty()
  3. 展示所有节点show_items(),结果可用list()转换
  4. 获取元素get_value_by_index(),以偏移量获取元素的值,超出偏移量会报错IndexErorr,可以是负偏移
  5. 在指定index的前面插入节点insert_before(),当超过实际长度,在最后插入;当为负数倒序选择插入,负数的绝对值大于实际长度则在最前面插入
  6. 在指定index之后插入节点insert_after()
  7. 在链表尾部追加append()
  8. 正序遍历traverse_forward(),定义遍历方法,要改写到类中的__iter__()和__next__()
  9. 反序遍历traverse_backward(),类似正序遍历
  10. 移除指定index元素remove(),偏移可以为负偏移,但超出偏移范围会报错
  11. 判断元素是否存在is_exist()
  12. 查找指定元素第一次出现的偏移indexOf()
  13. 清空链表clear()
  14. 链表的反转reverse() 点我跳到链表反转

    代码实现
  • 定义节点
class Node:"""The nodes of double linked list"""def __init__(self, item):self.prev = Noneself.item = itemself.next = None
  • 重写正向遍历方法:(不记得的同学可以参考两个示例:重写遍历方法示例)
class ForwardIterator:"""Define a forward-direction iterator"""def __init__(self, node):self.head = node.headself.cur = self.headdef __iter__(self):return selfdef __next__(self):"""Return the next item's value of iterator"""try:self.temp = self.curself.cur = self.cur.nextreturn self.temp.itemexcept AttributeError as e:raise StopIteration
  • 重写反向遍历方法:(跟正序遍历差不多,只是指针的名字不同)
class ReversalIterator:"""Define a reverse-direction iterator"""def __init__(self, tail):self.cur = taildef __iter__(self):return selfdef __next__(self):"""Return the next item's value of iterator"""try:self.temp = self.curself.cur = self.cur.prevreturn self.temp.itemexcept AttributeError as e:raise StopIteration

  • 功能实现
class DoubleLinkedList:def __init__(self):"""Initialize the head and the length of double linked list"""self.head = Noneself.len = 0def is_empty(self):"""Judge if a linked list is empty"""return self.head is Nonedef show_items(self):"""Show all the elements of linked list"""if self.is_empty():return Nonecur = self.headwhile cur.next:yield cur.itemcur = cur.nextyield cur.itemdef get_value_by_index(self, index):"""Get a value by index"""cur = self.headindex = self.len + index if index < 0 else indexif index < 0:raise IndexError('index out of range')try:for i in range(index):cur = cur.nextreturn cur.itemexcept AttributeError as e:raise IndexError('index out of range')# def length(self):#     """Return the elements number of a linked list"""#     return self.lendef insert_before(self, index, item):"""Insert an element before the given index of a node"""node = Node(item)cur = self.headif not cur:self.head = nodeelse:if -self.len < index < 0:index += self.lenprint(index)if index > 0:  # 0 < index or 0 < (index + self.len)while cur.next:index -= 1if index <= 0:breakcur = cur.nextif cur.next:node.next = cur.nextcur.next.prev = nodecur.next = nodenode.prev = curelse:# node.next = cur.next    # Not necessarycur.next = nodenode.prev = curelif index <= -self.len or index == 0:   # index < -self.len or index == 0node.next = self.headself.head.prev = nodeself.head = nodeself.len += 1def insert_after(self, index, item):"""Insert an element after the given index of a node"""if index >= 0:self.insert_before(index+1, item)elif -self.len-1 <= index < 0:  # insert_before(self.len+index+1) equals to insert_after(index)self.insert_before(self.len+index+1, item)else:self.insert_before(0, item)# Use insert_before(), so as to no self.len+=1def append(self, item):"""Append an element to the end of a linked list"""node = Node(item)if self.is_empty():self.head = nodeelse:cur = self.headwhile cur.next:cur = cur.next# node.next = cur.next  # Not necessarycur.next = nodenode.prev = curself.len += 1def traverse_forward(self):"""Travel from the head and return the node one by one"""return ForwardIterator(self)def traverse_backward(self):"""Travel from the tail and return the node one by one"""cur = self.headwhile cur.next:cur = cur.nextreturn ReversalIterator(cur)def remove(self, index):"""Remove an element by index"""if not -self.len - 1 < index < self.len:raise IndexError('remove index out of range')if index < 0:index += self.lenif index == 0:self.head = self.head.nextelse:cur = self.headpre = curwhile index > 0:pre = curcur = cur.nextindex -= 1pre.next = cur.nextself.len -= 1def is_exist(self, item):"""Judge if an element in linked list"""return item in self.show_items()def indexOf(self, item):"""Find the first-appears-index of Node(item)"""return list(self.show_items()).index(item)def clear(self):"""CLear all nodes"""self.head = Noneself.len = 0def reverse(self):"""Reverse the linked list"""cur = self.headif not cur:returndef reverse(cur):if not cur.next:self.head = curreturn curprev = reverse(cur.next)prev.next = curcur.next = Nonereturn curreverse(cur)
  • 验证功能
if __name__ == '__main__':DLL = DoubleLinkedList()print(f"Is empty? {DLL.is_empty()}")for i in range(5):DLL.append(i)print(f"Show all items:{list(DLL.show_items())}")_index, _item = -6, 11DLL.insert_before(_index, _item)print(f"Insert {_item} before linked list[{_index}]: {list(DLL.show_items())}")_index, _item = -3, 22DLL.insert_after(_index, _item)print(f"Insert {_item} after linked list[{_index}]: {list(DLL.show_items())}")_item = 33DLL.append(_item)print(f"Append an element[{_item}] to the end: {list(DLL.show_items())}")import collections# Travel backwardprint("DLL.traverse_backward() is Iterable??", isinstance(DLL.traverse_backward(), collections.Iterable))print([i for i in iter(DLL.traverse_backward())])# for i in DLL.traverse_forward():#     print(i)# Travel forwardprint("DLL.traverse_forward() is Iterable??", isinstance(DLL.traverse_forward(), collections.Iterable))print([i for i in iter(DLL.traverse_forward())])# for i in DLL.traverse_forward():#     print(i)_index = -1DLL.remove(_index)print(f"Remove an element by index[{_index}]: {list(DLL.show_items())}")_item = 22print(f"Is item [{_item}] exists in this list? {DLL.is_exist(_item)}")_item = 1print(f"The first-appears-index of item [{_item}] in this list is {DLL.indexOf(_item)}")print(f"Clear all: {DLL.clear()}, the length of the list: {DLL.len} ")for i in range(3):DLL.append(i)print(f"Before reverse: {list(DLL.show_items())}")DLL.reverse()print(f"After reverse: {list(DLL.show_items())}")
  • 输出结果:
Is empty? True
Show all items:[0, 1, 2, 3, 4]
Insert 11 before linked list[-5]: [11, 0, 1, 2, 3, 4]
Insert 22 after linked list[5]: [11, 0, 1, 2, 3, 4, 22]
Append an element[33] to the end: [11, 0, 1, 2, 3, 4, 22, 33]
DLL.traverse_backward() is Iterable?? True
[33, 22, 4, 3, 2, 1, 0, 11]
DLL.traverse_forward() is Iterable?? True
[11, 0, 1, 2, 3, 4, 22, 33]
Remove an element by index[-1]: [11, 0, 1, 2, 3, 4, 22]
Is item [22] exists in this list? True
The first-appears-index of item [1] in this list is 2
[11, 0, 1, 55, 2, 3, 4, 22]
Clear all: None, the length of the list: 0 
Before reverse: [0, 1, 2]
After reverse: [2, 1, 0]

时间复杂度分析 点击回到代码实现

  • show_items()每次查询都需要遍历整条链表,while循环n次,时间复杂度为O(n)
  • get_value_by_index()每一次获取元素,都需要从头结点开始,向后for循环遍历,循环次数与查询的index成正比,为n次,时间复杂度为O(n)
  • insert_before()最坏情况是在链表最后插入,需要while循环n次,时间复杂度为O(n)
  • insert_after()同insert_before(),最多需要循环n次,时间复杂度为O(n)
  • append()每次追加都要循环完毕列表,执行n次,时间复杂度为O(n)
  • remove()最坏情况下需要循环n次,时间复杂度为O(n)
    在这里插入图片描述
    总结
  • 相较于顺序表,链表的插入和删除操作时间复杂度虽然也是O(n),但是实际链表操作的动作更加简洁,因为链表的内存地址无需连续,大小随时可变,在插入和删除时无需改变容量来调整合适的大小,此外插入和删除时,其他的元素也不用改变位置,因此当查询和插入操作较多时,推荐使用链表
  • 如果实际情况中使用查询操作较多时,链表相对顺序表而言操作性能会更低,此时建议使用顺序表

    附加——链表的反转 点击回到代码实现
def reverse(self):"""Reverse the linked list"""cur = self.headif not cur:returndef reverse(cur):if not cur.next:self.head = curreturn curprev = reverse(cur.next)prev.next = curcur.next = Nonereturn curreverse(cur)

链表的反转通过递归实现将链表的head移动到尾节点
递归算法所体现的“重复”一般有三个要求:

  • 一是每次调用在规模上都有所缩小(通常是减半);
  • 二是相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入);
  • 三是在问题的规模极小时必须用直接给出解答而不再进行递归调用,因而每次递归调用都是有条件的(以规模未达到直接解答的大小为条件),无条件递归调用将会成为死循环而不能正常结束。

链表反转:在这里插入图片描述
分析递归达成的反转过程:
在这里插入图片描述

重写遍历方法示例

  • Example1
import collectionsclass MyIter(object):def __init__(self, num):self.num = numdef __next__(self):if self.num == 0:raise StopIterationself.num -= 1return self.numdef __iter__(self):return selffor i in MyIter(4):print(i)
print(iter(MyIter(4)))print(isinstance(MyIter(4), collections.Iterable))
  • Example2
# Example2
class Iteration:def __init__(self, iterable):   # [1, 2, 3, 4]self.iterable = iterableself.stop = len(iterable)self.start = 0def __iter__(self):return selfdef __next__(self):"""Return the next item's value of iterator"""if self.start < self.stop:start = self.startself.start += 1return self.iterable[start]else:raise StopIterationdef add(self, item):self.iterable.append(item)alist = [1, 2, 3, 4]
IT = Iteration(alist)
print(f"[{IT.__class__.__name__}] is iterable? {isinstance(IT, collections.Iterable)}")
print(f"[{IT.__class__.__name__}] is iterable? {iter(IT)}")
for i in IT:print(i)

结果只需要用isinstance(IT, collections.Iterable)来判断它两是否相等即可,为True则可遍历,为False则不可遍历;用iter(object)也可以判断,但是如果不可遍历会报错
回到代码实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/469583.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux驱动之ioctl

大部分驱动除了需要具备读写设备的能力之外&#xff0c;还需要具备对硬件控制的能力。 一、在用户空间&#xff0c;使用ioctl系统调用来控制设备&#xff0c;原型如下&#xff1a; int ioctl(int fd,unsigned long cmd,...); /* fd:文件描述符 cmd:控制命令 ...:可选参数:插入*…

数据结构链表之单链表的快慢指针——3

单链表之快慢指针 单链表的快慢指针简介 快慢指针指链表中定义两个指针&#xff0c;两个指针的移动速度一快一慢&#xff0c;一般快指针移动步长为慢指针的两倍 快慢指针适合解决的几个典型问题 中间值问题单向链表是否有环问题有环链表的入口问题 先定义一个简单的节点 …

【Pytorch神经网络基础理论篇】 04 线性代数

同学你好&#xff01;本文章于2021年末编写&#xff0c;已与实际存在较大的偏差&#xff01; 故在2022年末对本系列进行填充与更新&#xff0c;欢迎大家订阅最新的专栏&#xff0c;获取基于Pytorch1.10版本的理论代码(2023版)实现&#xff0c; Pytorch深度学习理论篇(2023版)…

Bootstrap实现弹出框和提示框效果代码

一、Bootstrap弹出框使用过JQuery UI应该知道&#xff0c;它里面有一个dialog的弹出框组件&#xff0c;功能也很丰富。与jQuery UI的dialog类似&#xff0c;Bootstrap里面也内置了弹出框组件。打开bootstrap 文档可以看到它的dialog是直接嵌入到bootstrap.js和bootstrap.css里面…

数据结构链表之循环链表——4

循环链表与约瑟夫问题 循环链表定义 定义&#xff1a;循环链表的定义十分简单&#xff0c;只需使一条单链表的尾部结点指向头结点&#xff0c;即可完成循环链表 循环链表的构建 class Node:def __init__(self, item):self.item itemself.next Nonefirst Node(aa) secon…

【Pytorch神经网络基础理论篇】 05 矩阵计算

同学你好&#xff01;本文章于2021年末编写&#xff0c;已与实际存在较大的偏差&#xff01; 故在2022年末对本系列进行填充与更新&#xff0c;欢迎大家订阅最新的专栏&#xff0c;获取基于Pytorch1.10版本的理论代码(2023版)实现&#xff0c; Pytorch深度学习理论篇(2023版)…

统计iOS项目的总代码行数的方法

1、打开终端&#xff0c; 2、用cd命令 定位到工程所在的目录&#xff0c;然后调用以下命名即可把每个源代码文件行数及总数统计出来&#xff1a; find . "(" -name "*.m" -or -name "*.mm" -or -name "*.cpp" -or -name "*.h&quo…

【Pytorch神经网络基础理论篇】 06 自动求导+导数与微分

同学你好&#xff01;本文章于2021年末编写&#xff0c;已与实际存在较大的偏差&#xff01; 故在2022年末对本系列进行填充与更新&#xff0c;欢迎大家订阅最新的专栏&#xff0c;获取基于Pytorch1.10版本的理论代码(2023版)实现&#xff0c; Pytorch深度学习理论篇(2023版)…

数据结构链表之栈,Python3简单实现——5

数据结构链表之栈 栈的概述 定义&#xff1a;栈是一种基于先进后出(FILO)的数据结构&#xff0c;是一种只能在一段进行插入和删除操作的特殊线性表。引入名词&#xff1a;将数据存入栈的动作称为压栈&#xff0c;将数据取出栈的动作称为弹栈栈的特点&#xff1a;先进入栈的元…

【Pytorch神经网络基础理论篇】 08 Softmax 回归 + 损失函数 + 图片分类数据集

同学你好&#xff01;本文章于2021年末编写&#xff0c;已与实际存在较大的偏差&#xff01; 故在2022年末对本系列进行填充与更新&#xff0c;欢迎大家订阅最新的专栏&#xff0c;获取基于Pytorch1.10版本的理论代码(2023版)实现&#xff0c; Pytorch深度学习理论篇(2023版)…

CI Weekly #11 | 微服务场景下的自动化测试与持续部署

又一周过去了&#xff0c;最近我们的工程师正在搞一个“大事情” ——「flow.ci 配置文件」&#xff0c;稍微剧透一下&#xff0c;这个功能预计会在春节前上线。详情请大家关注 flow.ci Changelog 或其他官方通知:) 本期 CI Weekly 收录了的CI/CD实践、微服务自动化测试与持续部…

数据结构链表之栈——解决括号匹配问题和逆波兰表达式求值问题——6

括号匹配问题和逆波兰表达式求值问题 基于上一节已经使用python代码对栈进行了简单的实现&#xff0c;这一节我们在其基础上解决两个常见的问题 案例 括号匹配问题(点我直接到代码实现)逆波兰表达式求值问题(点我直接到代码实现) 括号匹配问题 在给定的字符串中&#xff0…

Java_基础阶段笔记总结汇总

一、Java简介 1、Java语言平台性介绍 2、JDK_JRE_JVM的组成和作用 JVM: Java虚拟机&#xff0c;是专门用来运行Java程序的,但是不能单独安装 JRE: Java运行环境&#xff0c;包含JVM(Java虚拟机,是专门用来运行Java程序的)和核心类库 JDK: Java开发工具包&#xff0c;包含JRE和…

数据结构链表之队列,Python3实现——7

数据结构链表之队列 队列概述 定义&#xff1a;队列是一种基于先进先出(FIFO)的数据结构&#xff0c;队列只能在一段进行插入和删除操作的结构&#xff0c;第一个进入队列的元素在读取时会第一个被读取 队列可以使用顺序表(Python中列表)实现&#xff0c;也可以用链表实现&am…

IDEA上Debug调试全流程

一、什么是Debug模式 是供程序员使用的程序调试工具&#xff0c;它可以用于查看程序的执行流程&#xff0c;也可以用于追踪程序执行过程来调试程序。使用IDEA的断点调试功能&#xff0c;查看程序的运行过程 Debug调试窗口介绍。 二、Debug模式操作流程【应用】 能够使用断点调…

数据结构链表之符号表,Python3实现——8

数据结构链表之符号表 符号表的介绍 之前章节介绍的顺序表和链表都是一个节点储存一个元素的表&#xff0c;但在日常生活中我们还有很多一次需要储存成对或多个值的情况&#xff0c;例如&#xff1a; 符号表最主要的目的将一对元素&#xff0c;用一个键和一个值将其联系起来&…

OpenCV_01 简介+无版权安装+模块分析

OpenCV是应用广泛的开源图像处理库&#xff0c;我们以其为基础&#xff0c;介绍相关的图像处理方法&#xff1a;包括基本的图像处理方法&#xff1a;几何变换&#xff0c;形态学变换&#xff0c;图像平滑&#xff0c;直方图操作&#xff0c;模板匹配&#xff0c;霍夫变换等&…

OpenCV_02 图像的基本操作:图像IO+绘制图形+像素点+属性+图像通道+色彩空间的改变

1 图像的IO操作 这里我们会给大家介绍如何读取图像&#xff0c;如何显示图像和如何保存图像。 1.1 读取图像 API cv.imread()参数&#xff1a; 要读取的图像 读取方式的标志 cv.IMREAD*COLOR&#xff1a;以彩色模式加载图像&#xff0c;任何图像的透明度都将被忽略。这是默…

数据结构之树:树的介绍——9

数据结构之树&#xff0c;介绍篇 树的基本定义 介绍&#xff1a;树&#xff08;tree&#xff09;是计算机中非常重要的数据结构&#xff0c;它的外形看起来像一颗倒挂着的的树&#xff0c;使用树这种结构可以描述生活中很多的事物&#xff0c;如族谱&#xff0c;单位的组织架…

OpenCV_03 图像的算数操作:图像的加法+图像的混合

1.图像的加法 你可以使用OpenCV的cv.add()函数把两幅图像相加&#xff0c;或者可以简单地通过numpy操作添加两个图像&#xff0c;如res img1 img2。两个图像应该具有相同的大小和类型&#xff0c;或者第二个图像可以是标量值。 注意&#xff1a;OpenCV加法和Numpy加法之间存…