版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
摘要
- 所谓数据都是要存储在内存上的,而内存则是一个连续的存储空间。
- 本文使用数据基本类型,实现链表的三种形式(链表不仅仅这三种形式)。
- 需要定义两个类,一个是节点类,一个是实现类。
- python中的列表使用的数据结构是:顺序表。而且具备以下几个特点:–分离式存储(数据区变化,表头信息是不变的),–元素外置(支持不同数据类型的存储和删改),–动态顺序表(扩充策略是:加倍)。
- 双向链表还有继续拓展的空间:例如双向循环链表。
- 理解代码需要参考以下几点:
1、先算等号右边的
2、等号只是引用
3、一切皆对象
4、一个节点的第二个属性,存储了下个节点的引用。
5、核心思想是:改变节点中链接区的指向。
6、若想动节点,先动自己的节点(要操作的数据),链表上的节点晚点动(原本的数据)。
代码片1:单向链表
# coding:utf-8
class Node(object):
'''节点'''
def __init__(self, elem):
# 数据区
self.elem = elem
# 连接区
self.next = None
class SingleLinkList(object):
'''单链表'''
def __init__(self, node=None): # node默认为none
self.__head = node # 私有属性,对象无法访问,可以导入
# self._head = 1 # 禁止导入,可以访问
def is_empty(self):
'''链表是否为空'''
# print(node.elem)
return self.__head is None # 这里是一个判断
def length(self):
"""链表长度"""
# cur游标,用来移动遍历节点
cur = self.__head
count = 0
# cur.next == None不可作为判断条件,因为链接区为None的时候,数据区实际上是有数据的,如果使用这个条件,那么这个数据将不被计入,游标也不会加1。
# 但是当节点为None时,意味着数据区已经没有数据,而且之前的那个cur.next == None时的数据区的数据也已经被计入了。
while cur is not None:
count += 1
cur = cur.next # 游标运动的过程
return count
def travel(self):
"""遍历整个列表"""
cur = self.__head
while cur is not None:
print(cur.elem, end=' ')
cur = cur.next
print('')
def add(self, item):
"""链表头部添加元素,头插法"""
node = Node(item)
node.next = self.__head
self.__head = node
def append(self, item):
"""尾部添加元素,尾插法"""
node = Node(item) # 这个时候node已经实例化了,(也就具备节点所对应的两个属性了)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next is not None:
cur = cur.next # 链接区一直指向下一个节点
cur.next = node # 将本为空的这个链接区,指向下个节点,使之不为空。
def insert(self, pos, item):
"""
指定位置插入元素
:param pos:从零开始,在哪添加元素
:param item: 添加的元素
"""
if pos <= 0:
self.add(item)
elif pos > self.length() - 1: # pos相当于索引,是要按照索引的级别进行比较的(而且不能相等--相等的时候意为在最后一个元素的前面添加一个元素,是插入,而不是追加)
self.append(item)
else:
pre = self.__head
count = 0
while count < (pos - 1): # 需要让添加的元素变为pos对应的索引,所以需要操作原本的该索引对应的前一个。
count += 1
pre = pre.next
node = Node(item)
# 当循环退出后 pre指向pos-1位置
node.next = pre.next
pre.next = node
def remove(self, item):
"""删除节点"""
pre = self.__head
if pre.elem == item:
self.__head = pre.next # 头节点换为头节点链接的下一个节点。
else:
while pre.next is not None:
if pre.next.elem == item:
pre.next = pre.next.next
else:
pre = pre.next
def remove_2(self, item):
'''删除节点方法2'''
cur = self.__head
pre = None
while cur is not None:
if cur.elem == item:
if cur == self.__head:
self.__head = cur.next
else:
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
def search(self, item):
"""查找节点是否存在"""
cur = self.__head
while cur is not None:
if item == cur.elem:
return True
else:
cur = cur.next
return False
def __call__(self, *args, **kwargs):
'''打印该列表'''
cur = self.__head
if cur is None:
return None
else:
s_tr = ''
while cur is not None:
if cur.next is not None:
s_tr += str(cur.elem) + ','
else:
s_tr += str(cur.elem)
cur = cur.next
s_tr = '<' + s_tr + '>'
return s_tr
if __name__ == '__main__':
ll = SingleLinkList()
print(ll.is_empty())
print(ll.length())
print(ll())
ll.append(1)
print(ll.is_empty())
print(ll.length())
ll.append(2)
ll.append(3)
ll.append(4)
ll.append(5)
ll.append(6)
ll.add(2)
ll.insert(2, 7)
print(ll.search(7))
ll.remove(2)
ll.remove_2(7)
ll.travel()
print(ll())
代码片2:双向链表
# coding:utf-8
class Node(object):
"""节点"""
def __init__(self, item):
self.elem = item
self.next = None
self.prev = None
class DoubleLinkList(object):
"双链表"
def __init__(self, node=None): # node默认为none
self.__head = node # 私有属性,对象无法访问,可以导入
# self._head = 1 # 禁止导入,可以访问
def is_empty(self):
'''链表是否为空'''
return self.__head is None # 这里是一个判断
def length(self):
"""链表长度"""
# cur游标,用来移动遍历节点
cur = self.__head
count = 0
# cur.next == None不可作为判断条件,因为链接区为None的时候,数据区实际上是有数据的,如果使用这个条件,那么这个数据将不被计入,游标也不会加1。
# 但是当节点为None时,意味着数据区已经没有数据,而且之前的那个cur.next == None时的数据区的数据也已经被计入了。
while cur is not None:
count += 1
cur = cur.next # 游标运动的过程
return count
def travel(self):
"""遍历整个列表"""
cur = self.__head
while cur is not None:
print(cur.elem, end=' ')
cur = cur.next
print('')
def add(self, item):
"""链表头部添加元素,头插法"""
node = Node(item)
node.next = self.__head
self.__head = node
node.next.prev = node
def append(self, item):
"""尾部添加元素,尾插法"""
node = Node(item) # 这个时候node已经实例化了,(也就具备节点所对应的两个属性了)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next is not None:
cur = cur.next # 链接区一直指向下一个节点
cur.next = node # 将本为空的这个链接区,指向下个节点,使之不为空。
node.prev = cur
def insert(self, pos, item):
"""
指定位置插入元素
:param pos:从零开始,在哪添加元素
:param item: 添加的元素
"""
if pos <= 0:
self.add(item)
elif pos > self.length() - 1: # pos相当于索引,是要按照索引的级别进行比较的(而且不能相等--相等的时候意为在最后一个元素的前面添加一个元素,是插入,而不是追加)
self.append(item)
else:
cur = self.__head
count = 0
while count < pos:
count += 1
cur = cur.next
# 当循环退出后 cur指向pos位置
node = Node(item)
node.next = cur
node.prev = cur.prev
cur.prev.next = node
cur.prev = node
def remove_2(self, item):
'''删除节点方法2'''
cur = self.__head
while cur is not None:
if cur.elem == item:
if cur == self.__head:
self.__head = cur.next
if cur.next: # 判断链表是否只有一个节点
cur.next.prev = None
else:
cur.prev.next = cur.next
if cur.next:
cur.next.prev = cur.prev
break
else:
cur = cur.next
def search(self, item):
"""查找节点是否存在"""
cur = self.__head
while cur is not None:
if item == cur.elem:
return True
else:
cur = cur.next
return False
if __name__ == '__main__':
ll = DoubleLinkList()
print(ll.is_empty())
print(ll.length())
ll.append(1)
print(ll.is_empty())
print(ll.length())
ll.append(2)
ll.append(3)
ll.append(4)
ll.append(5)
ll.append(6)
ll.add(2)
ll.insert(2, 7)
print(ll.search(7))
ll.remove_2(2)
ll.remove_2(7)
ll.travel()
代码片3:单向循环链表
# coding:utf-8
class Node(object):
'''节点'''
def __init__(self, elem):
# 数据区
self.elem = elem
# 连接区
self.next = None
class SingleCycleLinkList(object):
'''单向循环链表'''
def __init__(self, node=None):
self.__head = node
if node:
node.next = node
def is_empty(self):
'''链表是否为空'''
return self.__head is None # 这里是一个判断
def length(self):
"""链表长度"""
if self.is_empty():
return 0
else:
# cur游标,用来移动遍历节点
cur = self.__head
count = 1 # cur=self.__head的那个节点(尾节点)也是需要被计数的。
while cur.next != self.__head:
count += 1
cur = cur.next # 游标运动的过程
return count
def travel(self):
"""遍历整个列表"""
if self.is_empty():
return
else:
cur = self.__head
while cur.next != self.__head:
print(cur.elem, end=' ')
cur = cur.next
# 退出循环的那个节点(尾节点)的那个值也得打印
print(cur.elem)
def add(self, item):
"""链表头部添加元素,头插法"""
node = Node(item)
if self.is_empty():
self.__head = node
node.next = node
# return
else:
cur = self.__head
while cur.next != self.__head:
cur = cur.next
# 退出循环就代表游标已经处在尾节点了。
node.next = self.__head
self.__head = node
# cur.next =
cur.next = self.__head
def append(self, item):
"""尾部添加元素,尾插法"""
node = Node(item) # 这个时候node已经实例化了,(也就具备节点所对应的两个属性了)
if self.is_empty():
self.__head = node
node.next = node
else:
cur = self.__head
while cur.next != self.__head:
cur = cur.next # 链接区一直指向下一个节点
# cur.next=node
# node.next=self.__head
node.next = self.__head # 等于 node.next = cur.next
cur.next = node
# node=Node(item)
# if self.is_empty():
# self.__head=node
# node.next=self.__head
# else:
# cur=self.__head
# while cur.next != self.__head:
# cur=cur.next
# node.next=self.__head
# cur.next=node
def insert(self, pos, item):
"""
指定位置插入元素
:param pos:从零开始,在哪添加元素
:param item: 添加的元素
"""
if pos <= 0:
self.add(item)
elif pos > self.length() - 1: # pos相当于索引,是要按照索引的级别进行比较的(而且不能相等--相等的时候意为在最后一个元素的前面添加一个元素,是插入,而不是追加)
self.append(item)
else:
pre = self.__head
count = 0
while count < (pos - 1): # 需要让添加的元素变为pos对应的索引,所以需要操作原本的该索引对应的前一个。
count += 1
pre = pre.next
node = Node(item)
# 当循环退出后 pre指向pos-1位置
node.next = pre.next
pre.next = node
def remove_2(self, item):
'''删除节点方法2'''
if self.is_empty():
return
cur = self.__head
pre = None
while cur.next != self.__head:
if cur.elem == item:
if cur == self.__head:
# 头节点
# 找尾节点
rear = self.__head
while rear.next != self.__head:
rear = rear.next
self.__head = cur.next
rear.next = self.__head
else:
# 中间节点
pre.next = cur.next
# break
return # 为什么不用break?因为删完了就让该函数退出即可,break只能有终止此次循环的作用,有可能结束不了此程序
else:
pre = cur
cur = cur.next
# 退出循环代表尾节点
if cur.elem == item:
# 这个判断和上面的那个判断是否为头节点的语法不矛盾,因为只要退出循环了就说明cur.next==self.__head了,下面的这个判断,其实包含了两个判断语句。
# 而上面的那个,也是在cur.next != self.__head的条件下,判断 cur == self.__head的。
if cur == self.__head: # 此时代表链表中只有一个节点(尾节点也是头节点) if self.length()==1
self.__head = None
else:
pre.next = cur.next
def search(self, item):
"""查找节点是否存在"""
if self.is_empty():
return False # return 终止程序,所以不需要else
cur = self.__head
while cur.next != self.__head:
if item == cur.elem:
return True
else:
cur = cur.next
if cur.elem == item: # 判断尾节点
return True
else:
return False
if __name__ == '__main__':
ll = SingleCycleLinkList()
print(ll.is_empty())
print(ll.length())
ll.append(1)
print(ll.is_empty())
print(ll.length())
ll.append(2)
ll.append(3)
ll.append(4)
ll.append(5)
ll.append(6)
ll.add(2)
ll.insert(2, 7)
print(ll.search(7))
ll.remove_2(2)
ll.remove_2(7)
ll.travel()
print(ll.search(7))
补充
- 顺序表和链表的核心区别:顺序表需要一段连续的存储空间(可以通过计算便能得出数据所在得位置),而链表则是利用了节点的指向,从而可以存储在零散的内存上(只能利用指向来获取对应数据得位置)。
- 因为数据结构的不同,所以,显而易见的是:顺序表和链表修改数据时得时间复杂度是不同的。(具体参考百度一下)
- 栈和队列需要依靠基本的“容器”来设计出来(可以是链表,也可以是顺序表)
- 栈的特点:只有一端进出,先进后出(像杯子)
- 队列的特点:一端进,一端出,先进先出(像管道)。
- 本文仅服务作者本人