Python与数据结构[0] -> 链表/LinkedList[0] -> 单链表与带表头单链表的 Python 实现
单链表 / Linked List
目录
链表是一种基本的线性数据结构,在C语言中,这种数据结构通过指针实现,由于存储空间不要求连续性,因此插入和删除操作将变得十分快速。下面将利用Python来完成单链表的实现。
1 单链表
不带表头的单链表通常形式如下,
node_1 -> node_2 -> node_3 -> node_4
完整代码
class Node:
def __init__(self, val=None, nxt=None):
self.value = val
self.next = nxt def __str__(self):
return str(self.value) class LinkedList:
"""
Linked list:
node_1 -> node_2 -> node_3 -> node_4
"""
def __init__(self, iterable=()):
self.header = None
if iterable:
self.init(iterable) def __str__(self):
def _traversal(self):
node = self.header
while node and node.next:
yield node
node = node.next
yield node
return '->'.join(map(lambda x: str(x), _traversal(self))) def init(self, iterable=()):
# Note: use empty tuple rather than list to init iterable
if not iterable:
return
self.header = Node(iterable[0]) # header value
node = self.header
for i in iterable[1:]: # add all node
node.next = Node(i)
node = node.next def show(self):
print(self) @property
def length(self):
if self.header is None:
return 0
node = self.header # node pointer points to header
i = 1
while node.next:
node = node.next # node pointer move to next
i += 1
return i @property
def is_empty(self):
return self.header is None def clear(self):
self.__init__()
# self.header = None def append(self, item):
self.insert(item, self.length) def find(self, item):
node = self.header
while node.next and node.value != item:
node = node.next
if node.value == item:
return node
return None def find_previous(self, item):
node = self.header
while node.next and node.next.value != item:
node = node.next
if node.next and node.next.value == item:
return node
return None def delete(self, item):
'''
node_1 -- X --> node_2 -----> node_3
\ /
\ /
------------------
'''
prev = self.find_previous(item)
if prev:
prev.next = prev.next.next def insert(self, item, index):
'''
----> node_2 ---
/ \
/ \
node_1 ------- X ---------> node_3 '''
if abs(index) > self.length:
return
if index < 0:
self.insert(item, self.length+index+1)
return
elif index == 0:
self.insert(self.header.value, 1)
self.header.value = item
return
node = self.header
i = 0
while i < index-1:
node = node.next
i += 1
n = node.next
node.next = Node(item, n) def test(li):
print('Show linked list:')
li.show() print('\nInit linked list:')
li.init([1, 2, 3, 4, 5, 6, 'xd', 8, 9])
li.show() print('\nInsert element:')
li.insert('xxd', -3)
li.show() print('\nAppend element:')
li.append('')
li.show() e = 'xd'
print('\nFind element:')
x = li.find(e)
print(x.value if x else x)
print('\nFind previous element:')
x = li.find_previous(e)
print(x.value if x else x) print('\nDelete element:')
li.delete('xd')
li.show() print('\nFind element not exist:')
x = li.find(e)
print(x.value if x else x) print('\nInsert element to header:')
li.insert('cc', 0)
li.show() print('\nClear linked list:')
li.clear()
li.show() print('\nCurrent length: %s' % li.length)
print('\nIs empty: %s' % li.is_empty) if __name__ == '__main__':
test(LinkedList())
分段解释
首先,需要定义一个结点类(在C语言中则使用结构体进行定义),其中包括两个基本属性,当前值和下个结点。由于Python中无法使用类似C语言中的指针,因此只能通过变量的引用来实现类似指针操作的功能。为了更好的显示结点,这里重载了结点的内置函数__str__。
class Node:
def __init__(self, val=None, nxt=None):
self.value = val
self.next = nxt def __str__(self):
return str(self.value)
接着,我们需要。定义一个链表类,并利用前面的结点来构建这个链表。链表类中包含了一个头结点属性,在初始化函数中,首先将头结点赋值None,类似于在C语言中将头结点指针指向NULL。初始化方法中定义了一个默认参数,使得链表可以在实例化的时候利用传入的可迭代对象进行链表生成。
class LinkedList:
"""
Linked list:
node_1 -> node_2 -> node_3 -> node_4
"""
def __init__(self, iterable=()):
self.header = None
if iterable:
self.init(iterable)
重载链表的__str__方法,在显示链表的时候会对链表进行遍历,然后以更加清晰的方式显示在输出终端上。
def __str__(self):
def _traversal(self):
node = self.header
while node and node.next:
yield node
node = node.next
yield node
return '->'.join(map(lambda x: str(x), _traversal(self)))
定义链表的生成函数,函数中接收可迭代对象并生成新的链表。注意这里最好使用不可变的元组而不是可变的列表。
在生成时,首先将第一个元素作为头结点,然后依次将每个结点的next属性引用指向下一个结点。
def init(self, iterable=()):
# Note: use empty tuple rather than list to init iterable
if not iterable:
return
self.header = Node(iterable[0]) # header value
node = self.header
for i in iterable[1:]: # add all node
node.next = Node(i)
node = node.next
链表的show方法,由于已经重载了__str__方法,这里只需要print自身即可。
def show(self):
print(self)
链表的length属性方法,用于返回链表的长度,利用装饰器定义成属性方法,调用时遍历链表计算长度最后返回即可。
@property
def length(self):
if self.header is None:
return 0
node = self.header # node pointer points to header
i = 1
while node.next:
node = node.next # node pointer move to next
i += 1
return i
链表的is_empty属性方法,用于检测链表是否为空,只需要查看头结点是否为None即可。
@property
def is_empty(self):
return self.header is None
链表的clear方法,用于清空链表,可以选择直接初始化链表或将链表的头结点指向None。
def clear(self):
self.__init__()
# self.header = None
链表的append方法,用于在链表尾部添加一个元素结点,这里只要直接调用后面的insert方法并插入到链表长度的那个位置即可。
def append(self, item):
self.insert(item, self.length)
链表的find方法,用于在链表中查找并返回某个结点,查找时会从链表的头部开始,遍历查找,直到找到查找的值或遇到None,这里的find方法有两点需要注意:
- 查找是依据元素的值进行查找,返回找到的第一个结点;
- 这里的查找方法如果遇到有环链表将无法自行检测退出
最后返回查找到的结点或None。
def find(self, item):
node = self.header
while node.next and node.value != item:
node = node.next
if node.value == item:
return node
return None
find_previous与find类似,返回查找结点的前一个结点。
def find_previous(self, item):
node = self.header
while node.next and node.next.value != item:
node = node.next
if node.next and node.next.value == item:
return node
return None
delete方法用于删除结点,基本思路在于,找到需要删除结点node_2的前一个结点node_1,将前一个结点node_1的next指针指向删除结点的下一个结点node_3,从而惰性删除了这个结点。
查找前一个结点可以使用前面定义的find_previous方法。
def delete(self, item):
'''
node_1 -- X --> node_2 -----> node_3
\ /
\ /
------------------
'''
prev = self.find_previous(item)
if prev:
prev.next = prev.next.next
insert方法用于向链表中插入一个结点,
基本思路在于,找到需要插入的位置前后的两个结点node_1和node_3,将node_1.next指向需要插入的结点node_2,再将node_2.next指向node_3,便完成了插入操作。
若插入的位置为头结点之前,则会替换原来的头结点为新插入的结点。
同时,这里的插入函数还支持index为负数的情况,类似于列表的负数索引。
def insert(self, item, index):
'''
----> node_2 ---
/ \
/ \
node_1 ------- X ---------> node_3 '''
if abs(index) > self.length:
return
if index < 0:
self.insert(item, self.length+index+1)
return
elif index == 0:
self.insert(self.header.value, 1)
self.header.value = item
return
node = self.header
i = 0
while i < index-1:
node = node.next
i += 1
n = node.next
node.next = Node(item, n)
最后,定义一个测试函数用于测试链表的各种操作。
开始时显示空表
def test(li):
print('Show linked list:')
li.show()
得到结果
Show linked list:
None
接着初始化链表元素
print('\nInit linked list:')
li.init([1, 2, 3, 4, 5, 6, 'xd', 8, 9])
li.show()
得到结果
Init linked list:
1->2->3->4->5->6->xd->8->9
再向链表中插入元素
print('\nInsert element:')
li.insert('xxd', -3)
li.show()
得到结果
Insert element:
1->2->3->4->5->6->xd->xxd->8->9
向链表末尾添加元素
print('\nAppend element:')
li.append('')
li.show()
得到结果
Append element:
1->2->3->4->5->6->xd->xxd->8->9->10
查找元素
e = 'xd'
print('\nFind element:')
x = li.find(e)
print(x.value if x else x)
print('\nFind previous element:')
x = li.find_previous(e)
print(x.value if x else x)
得到结果
Find element:
xd Find previous element:
6
删除元素
print('\nDelete element:')
li.delete('xd')
li.show()
得到结果
Delete element:
1->2->3->4->5->6->xxd->8->9->10
查找不存在的元素
print('\nFind element not exist:')
x = li.find(e)
print(x.value if x else x)
得到结果
Find element not exist:
None
替换头结点
print('\nInsert element to header:')
li.insert('cc', 0)
li.show()
得到结果
Insert element to header:
cc->1->2->3->4->5->6->xxd->8->9->10
清空链表并查看链表信息
print('\nClear linked list:')
li.clear()
li.show() print('\nCurrent length: %s' % li.length)
print('\nIs empty: %s' % li.is_empty)
得到结果
Clear linked list:
None Current length: 0 Is empty: True
2 带表头单链表
这种单链表带有一个标志结点,通常被称为表头或哑结点,表头通常位于位置0处,通常不会被改变。
Linked list with dummy header node:
Header -> node_1 -> node_2 -> node_3
带表头的单链表在实现时与不带表头的单链表类似,因此可以继承前面的单链表类来派生出一个带表头单链表类,其中需要进行修改和重载的方法主要有生成链表、判断链表为空和插入链表这几个方法。
from linked_list import Node, LinkedList, test class LinkedListDummyHeader(LinkedList):
"""
Linked list with dummy header node:
Header -> node_1 -> node_2 -> node_3
"""
def __init__(self, iterable=()):
self.header = Node('Header', None)
if iterable:
self.init(iterable) def init(self, iterable=()):
if not iterable:
return
node = self.header
for i in iterable:
node.next = Node(i)
node = node.next @property
def is_empty(self):
return self.header.next is None
# if self.length == 1:
# return True
# return False def insert(self, item, index):
if index == 0:
return
super(LinkedListDummyHeader, self).insert(item, index) if __name__ == '__main__':
test(LinkedListDummyHeader())
__init__方法:
初始化时头结点不再指向None,而是指向表头。
init方法:
由于有表头的存在,因此创建初始链表的方法首先需要建立一个表头,再将所有元素依次加入链表中。
is_empty方法:
由于表头的存在,此时判断是否为空则可以根据表头指向结点是否为空,或链表长度是否为1来进行。
insert方法:
此处需要修改的地方在于当插入点为表头时,则应该直接返回或引发异常,无法修改表头。
最后利用单链表的测试函数进行测试得到结果
Show linked list:
Header Init linked list:
Header->1->2->3->4->5->6->xd->8->9 Insert element:
Header->1->2->3->4->5->6->xd->xxd->8->9 Append element:
Header->1->2->3->4->5->6->xd->xxd->8->9->10 Find element:
xd Find previous element:
6 Delete element:
Header->1->2->3->4->5->6->xxd->8->9->10 Find element not exist:
None Insert element to header:
Header->1->2->3->4->5->6->xxd->8->9->10 Clear linked list:
Header Current length: 1 Is empty: True
Python与数据结构[0] -> 链表/LinkedList[0] -> 单链表与带表头单链表的 Python 实现的更多相关文章
- Python与数据结构[1] -> 栈/Stack[0] -> 链表栈与数组栈的 Python 实现
栈 / Stack 目录 链表栈 数组栈 栈是一种基本的线性数据结构(先入后出FILO),在 C 语言中有链表和数组两种实现方式,下面用 Python 对这两种栈进行实现. 1 链表栈 链表栈是以单链 ...
- Python与数据结构[2] -> 队列/Queue[0] -> 数组队列的 Python 实现
队列 / Queue 数组队列 数组队列是队列基于数组的一种实现,其实现类似于数组栈,是一种FIFO的线性数据结构. Queue: <--| 1 | 2 | 3 | 4 | 5 |<-- ...
- [H5表单]html5自带表单验证体验优化及提示气泡修改
慕课网之前录制的视频,js/jquery各种宽高的理解和应用,最近终于上线了.还有一个html5左侧导航没有上线!最近慕课网系列课程让我录制一个html5表单验证的课程.今天就稍微说一下表单验证!另外 ...
- Python与数据结构[4] -> 散列表[0] -> 散列表与散列函数的 Python 实现
散列表 / Hash Table 散列表与散列函数 散列表是一种将关键字映射到特定数组位置的一种数据结构,而将关键字映射到0至TableSize-1过程的函数,即为散列函数. Hash Table: ...
- Python与数据结构[3] -> 树/Tree[0] -> 二叉树及遍历二叉树的 Python 实现
二叉树 / Binary Tree 二叉树是树结构的一种,但二叉树的每一个节点都最多只能有两个子节点. Binary Tree: 00 |_____ | | 00 00 |__ |__ | | | | ...
- Python与数据结构[4] -> 散列表[2] -> 开放定址法与再散列的 Python 实现
开放定址散列法和再散列 目录 开放定址法 再散列 代码实现 1 开放定址散列法 前面利用分离链接法解决了散列表插入冲突的问题,而除了分离链接法外,还可以使用开放定址法来解决散列表的冲突问题. 开放定 ...
- Python与数据结构[4] -> 散列表[1] -> 分离链接法的 Python 实现
分离链接法 / Separate Chain Hashing 前面完成了一个基本散列表的实现,但是还存在一个问题,当散列表插入元素冲突时,散列表将返回异常,这一问题的解决方式之一为使用链表进行元素的存 ...
- Python与数据结构[0] -> 链表/LinkedList[1] -> 双链表与循环双链表的 Python 实现
双链表 / Doubly Linked List 目录 双链表 循环双链表 1 双链表 双链表和单链表的不同之处在于,双链表需要多增加一个域(C语言),即在Python中需要多增加一个属性,用于存储指 ...
- Python与数据结构[0] -> 链表/LinkedList[2] -> 链表有环与链表相交判断的 Python 实现
链表有环与链表相交判断的 Python 实现 目录 有环链表 相交链表 1 有环链表 判断链表是否有环可以参考链接, 有环链表主要包括以下几个问题(C语言描述): 判断环是否存在: 可以使用追赶方法, ...
随机推荐
- 《Cracking the Coding Interview》——第18章:难题——题目4
2014-04-29 01:05 题目:数数从0到n总共有多少个数字‘2’? 解法:数位动态规划,可以O(log10(n))时间内解决. 代码: // 18.4 Count the number of ...
- 新生 & 语不惊人死不休 —— 《无限恐怖》读后有感
开篇声明,我博客中“小心情”这一系列,全都是日记啊随笔啊什么乱七八糟的.如果一不小心点进来了,不妨直接关掉.我自己曾经写过一段时间的日记,常常翻看,毫无疑问我的文笔是很差的,而且心情也是瞬息万变的.因 ...
- CandyCrush 糖果传奇
1.unity自带触发事件 unity的每一个Collider对象都有类似OnMouseDown.OnMouseOver等事件.此事件是存在于MonoBehaviour脚本里的,而MonoBehavi ...
- 孤荷凌寒自学python第二十五天初识python的time模块
孤荷凌寒自学python第二十五天python的time模块 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 通过对time模块添加引用,就可以使用python的time模块来进行相关的时间操 ...
- opencv3.1+cmake+mingw5.3+QT5编译
太不容易了! 想要访问opencv的官网貌似要FQ才行.下载了opencv3.2版本,发现cmake在download opencv_ffmpeg.dll的地方超时了. 于是搜索一番,发现很多编译op ...
- springmvc和struts2的区别(转)
1.客户端浏览器发出HTTP请求. 2.根据web.xml配置,该请求被FilterDispatcher接收. 3.根据struts.xml配置,找到需要调用的Action类和方法, 并通过I ...
- [poj] 1236 networks of schools
原题 这是一道强连通分量板子题. 显然subtask1 是要输出入度为0的点的个数 而subtask2,我们考虑一下最优一定是把一个出度为零的点连到入度为零的点上,这样我们要输出的就是max(出度为零 ...
- (转)myeclipse工程 junit不能运行 ClassNotFoundException
博文转自:http://www.cnblogs.com/java-zone/articles/2730722.html myeclipse工程 junit不能运行 1 2 3 4 5 6 7 8 ...
- python之短路计算-布尔类型
Python中布尔类型 我们已经了解了Python支持布尔类型的数据,布尔类型只有True和False两种值,但是布尔类型有以下几种运算: 与运算:只有两个布尔值都为 True 时,计算结果才为 Tr ...
- 狂K 线段树
想写好树剖~~线段树very important HDU 1166 敌兵布阵 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536 ...