前言
以下是花圃在学习Python的数据结构与算法的一些要点内容笔记。
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入或删除的时候最快可以达到O(1)的复杂度(告诉节点地址的情况下),比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。
【程序本身很多可以优化的地方,只是供参考思路】
一、单向链表
之前我们提到的一种的数据结构是顺序表,顺序表实际上是向操作系统OS申请开辟一段连续的连续的存储空间,不管是外置还是不外置,那么我现在不希望我提前去估计我要用多大的空间,那么我们就引入了链表。
链表就是通过一个元素通过存储下一个元素的地址(位置)去将两者串联起来,这里注意无论是顺序表还是链表我们都不难发现,他们属于一维的线性表。如何实现通过上一个元素找到下一个元素位置???
我单独存储一个元素是无法找到下一个元素的。那么就需要我既要有数据,又有指向,那么这个时候数据存储的地方是数据区,紧接着会有一个指向下一个元素的指针,被称作链接区,这个时候两者合称结点。
单向列表的概念就是只有一个指向,单向存储,头部叫做头节点,会有一个指向头部存储的地址,尾部称作尾节点,尾节点的最后一个链接区地址是空。
那么我们知道单向列表既然属于数据结构,那么对于一种数据结构实际上是一对象,我们就可以把该对象以及操作方法封装成类。这实际上就是我们在顺序表里引入的ADT模型。
我们来试着实现这样的数据结构,实际上python中的元组类型完全可以满足节点的两两组合,但我们不去考虑python的特殊性,考虑一般程序的普适性。
# 定义节点类:(传入想要存储的数据)(数据区之外的链接区起始为空)
class Node(object):
def __init__(self,elem):
self.elem=elem
self.next=None
pass
pass
# 定义单向连接关系类,与支持操作封装到一起
class Singlelinklist(object):
def __init__(self,node=None):
self.__head=node
pass
def is_empty(self):
"""是否为空"""
return self.__head==None
def length(self):
"""长度"""
cur=self.__head
count=0
while cur!=None:
count+=1
cur=cur.next
pass
return count
def travel(self):
"""遍历"""
cur=self.__head
while cur!=None:
print(cur.elem,end=" ")
cur=cur.next
pass
print("\n")
pass
def append(self,item):
"""尾插法"""
node=Node(item)
if self.__head==None:
self.__head=node
pass
else:
cur = self.__head
while cur.next!=None:
cur=cur.next
pass
cur.next=node
pass
def add(self,item):
"""头插法"""
node=Node(item)
node.next=self.__head
self.__head=node
pass
def insert(self,pop,item):
"""任意位置插入"""
node=Node(item)
count=0
if pop<=0:
self.add(item)
pass
elif pop>self.length()-1:
self.append(item)
pass
else:
cur=self.__head
while cur!=None:
count+=1
cur=cur.next
if count==pop-1:
node.next = cur.next
cur.next = node
break
pass
pass
pass
def search(self,item):
"""寻找某个元素在链表中的位置"""
cur=self.__head
count=0
while cur!=None:
count+=1
if cur.elem==item:
return count-1,True
else:
cur=cur.next
pass
pass
return "不存在",False
def remove(self,item):
"""移除某个元素"""
cur=self.__head
pre=None
while cur!=None:
if cur.elem==item:
if cur==self.__head:
self.__head=cur.next
pass
else:
pre.next=cur.next
pass
break
pass
else:
pre=cur
cur=cur.next
pass
pass
pass
pass
if __name__=="__main__":
ll=Singlelinklist()
print(ll.is_empty())
print(ll.length())
#结果应该是True,0
print("=====================================")
ll.append(1)
print(ll.is_empty())
print(ll.length())
#结果应该是False,1
print("=====================================")
ll.append(2)
ll.append(3)
ll.append(4)
ll.travel()
# 结果应该是1 2 3 4
print("=====================================")
ll.add(5)
ll.travel()
# 结果应该是5 1 2 3 4
print("=====================================")
ll.insert(-1,6)
ll.travel()
#结果应该是6 5 1 2 3 4
ll.insert(10,7)
ll.travel()
# 结果应该是 6 5 1 2 3 4 7
ll.insert(2,100)
ll.travel()
# 结果应该是6 5 100 1 2 3 4 7
print("=====================================")
print(ll.search(100))
print(ll.search(200))
#结果应该是(2, True) ('不存在', False)
print("=====================================")
ll.remove(100)
ll.travel()
ll.remove(7)
ll.travel()
ll.remove(6)
ll.travel()
#结果应该是6 5 1 2 3 4 7 6 5 1 2 3 4 5 1 2 3 4
True
0
=====================================
False
1
=====================================
1 2 3 4
=====================================
5 1 2 3 4
=====================================
6 5 1 2 3 4
6 5 1 2 3 4 7
6 5 100 1 2 3 4 7
=====================================
(2, True)
('不存在', False)
=====================================
6 5 1 2 3 4 7
6 5 1 2 3 4
5 1 2 3 4
单链表与顺序表的对比:
访问: O(n) O(1)
头插法:O(1) O(n)
尾插法:O(n) O(1)
中间: O(n) O(n)
链表会多占内存,但是链表又是分散存取内存,顺序表必须是连续空间,不方便改动,比较大数据可能就需要申请很大的连续空间,而链表可以利用内存中离散的空间,在访问的时候,链表必须是顺序,而顺序表可以通过计算一步到位,而且注意虽然时间复杂度有的时候相同,但一个是遍历,另一个是内存空间数据的搬迁。
二、双向链表
这个时候我们需要引入两个概念,在单向链表中,有元素区有链接区,那么链接区指向的是下一个元素的位置,也就是说我想找某一个元素的上一个位置的元素,我依然要从头开始,那么有了双向结点就不同了,双向节点是有元素区、前驱区、后继区组成的,也就是我的后继区域仍然是当前指向下一个位置,前驱区域就是指向前一个区域的了,元素区存储数据。
到这里我们可以注意到,顺序表、单向链表、双向链表,我们都是在抽象一种数据类型,将数据类型的属性操作方法都封装,归为一类,称作抽象数据类型ADT。
class Node(object):
def __init__(self,elem):
self.elem=elem
self.next=None
self.prev=None
pass
pass
class Double_singlelinklist():
def __init__(self,node=None):
self.__head=node
pass
def is_empty(self):
"""是否为空"""
return self.__head==None
def length(self):
"""长度"""
cur=self.__head
count=0
while cur!=None:
count+=1
cur=cur.next
pass
return count
def travel(self):
"""遍历"""
cur=self.__head
while cur!=None:
print(cur.elem,end=" ")
cur=cur.next
pass
print("\n")
pass
def append(self,item):
"""尾插法"""
node=Node(item)
if self.__head==None:
self.__head=node
pass
else:
cur = self.__head
while cur.next!=None:
cur=cur.next
pass
cur.next=node
node.prev=cur #与单向不同1
pass
def add(self,item):
"""头插法"""
node=Node(item)
node.next=self.__head
self.__head=node
node.next.prev=node #与单向的不同2
#或者 self.__head.pre=node的方式置前
pass
def insert(self,pop,item):
"""任意位置插入"""
node=Node(item)
count=0
if pop<=0:
self.add(item)
pass
elif pop>self.length()-1:
self.append(item)
pass
else:
cur=self.__head
while cur!=None:
count+=1
cur=cur.next
if count==pop-1:
node.next = cur #与单向的方式不同3
node.prev=cur.prev
cur.prev.next=node
cur.prev=node
break
pass
pass
pass
def search(self,item):
"""寻找某个元素在链表中的位置"""
cur=self.__head
count=0
while cur!=None:
count+=1
if cur.elem==item:
return count-1,True
else:
cur=cur.next
pass
pass
return "不存在",False
def remove(self,item): #与单向不同4
"""移除某个元素"""
cur=self.__head
while cur!=None:
if cur.elem==item:
if cur==self.__head:
self.__head=cur.next
if cur.next==None:
cur.next.prev=None
pass
pass
else:
cur.prev.next=cur.next
if cur.next!=None:
cur.next.prev=cur.prev
pass
pass
break
pass
else:
cur=cur.next
pass
pass
pass
pass
if __name__=="__main__":
ll = Double_singlelinklist()
print(ll.is_empty())
print(ll.length())
# 结果应该是True,0
print("=====================================")
ll.append(1)
print(ll.is_empty())
print(ll.length())
# 结果应该是False,1
print("=====================================")
ll.append(2)
ll.append(3)
ll.append(4)
ll.travel()
# 结果应该是1 2 3 4
print("=====================================")
ll.add(5)
ll.travel()
# 结果应该是5 1 2 3 4
print("=====================================")
ll.insert(-1, 6)
ll.travel()
# 结果应该是6 5 1 2 3 4
ll.insert(10, 7)
ll.travel()
# 结果应该是 6 5 1 2 3 4 7
ll.insert(2, 100)
ll.travel()
# 结果应该是6 5 100 1 2 3 4 7
print("=====================================")
print(ll.search(100))
print(ll.search(200))
# 结果应该是(2, True) ('不存在', False)
print("=====================================")
ll.remove(100)
ll.travel()
ll.remove(7)
ll.travel()
ll.remove(6)
ll.travel()
# 结果应该是6 5 1 2 3 4 7 6 5 1 2 3 4 5 1 2 3 4
True
0
=====================================
False
1
=====================================
1 2 3 4
=====================================
5 1 2 3 4
=====================================
6 5 1 2 3 4
6 5 1 2 3 4 7
6 100 5 1 2 3 4 7
=====================================
(1, True)
('不存在', False)
=====================================
6 5 1 2 3 4 7
6 5 1 2 3 4
5 1 2 3 4
三、单向循环链表
class Node(object):
def __init__(self,elem):
self.elem=elem
self.next=None
pass
pass
class Singlecyclelinklist(object):
def __init__(self,node=None):
self.__head=node
if node:
node.next=node
pass
pass
def is_empty(self):
"""是否为空"""
return self.__head is None
def length(self):
"""长度"""
if self.is_empty():
return 0
else:
cur=self.__head
count=1
while cur.next!=self.__head:
count+=1
cur=cur.next
pass
return count
def travel(self):
"""遍历"""
if self.is_empty():
return 0
else:
cur=self.__head
while cur.next!=self.__head:
print(cur.elem,end=" ")
cur=cur.next
pass
print(cur.elem, end=" ")
print("\n")
pass
def append(self,item):
"""尾插法"""
node=Node(item)
if self.is_empty():
node.next=node
self.__head=node
pass
else:
cur = self.__head
while cur.next!=self.__head:
cur=cur.next
pass
node.next=self.__head
cur.next=node
pass
pass
def add(self,item):
"""头插法"""
node=Node(item)
if self.is_empty():
self.__head = node
node.next = node
pass
elif self.is_empty()==1:
node.next=self.__head
self.__head.next = node
self.__head=node
else:
cur=self.__head
while cur.next!=self.__head:
cur=cur.next
pass
cur.next=node
node.next=self.__head
self.__head=node
pass
def insert(self,pop,item):
"""任意位置插入"""
node=Node(item)
count=0
if pop<=0:
self.add(item)
pass
elif pop>self.length()-1:
self.append(item)
pass
else:
cur=self.__head
while cur.next!=self.__head:
count+=1
cur=cur.next
if count==pop-1:
node.next=cur.next
cur.next = node
break
pass
pass
pass
def search(self,item):
"""寻找某个元素在链表中的位置"""
if self.is_empty():
return "不存在",False
else:
cur=self.__head
count=0
while cur.next!=self.__head:
count+=1
if cur.elem==item:
return count-1,True
else:
cur=cur.next
pass
pass
if cur.elem==item:
return count,True
return "不存在",False
def remove(self,item):
"""移除某个元素"""
if self.is_empty():
return False
else:
cur=self.__head
pre=None
while cur.next!=self.__head:
if cur.elem==item:
if cur==self.__head:
cur1 = self.__head
while cur1.next!=self.__head:
cur1 = cur1.next
pass
self.__head=cur.next
cur1.next=self.__head
pass
else:
pre.next=cur.next
pass
return #注意这里不能用break,break只是退出循环,后面if不该执行的情况仍然会执行,所以应该是return退出整个
else:
pre=cur
cur=cur.next
pass
if cur.elem==item:
if cur==self.__head:
self.__head=None
pass
else:
pre.next=cur.next
pass
pass
pass
pass
pass
if __name__=="__main__":
ll=Singlecyclelinklist()
print(ll.is_empty())
print(ll.length())
#结果应该是True,0
print("=====================================")
ll.add(1)
ll.add(2)
ll.add(3)
ll.travel()
print("=====================================")
ll.append(4)
ll.append(5)
ll.append(6)
ll.travel()
print("=====================================")
ll.insert(-1,100)
ll.insert(10,200)
ll.insert(2,300)
ll.travel()
print("=====================================")
print(ll.search(300))
print(ll.search(4))
print(ll.search(11))
print("=====================================")
ll.remove(100)
ll.travel()
ll.remove(200)
ll.travel()
ll.remove(300)
ll.travel()
print("=====================================")
True
0
=====================================
3 2 1
=====================================
3 2 1 4 5 6
=====================================
100 3 300 2 1 4 5 6 200
=====================================
(2, True)
(5, True)
('不存在', False)
=====================================
3 300 2 1 4 5 6 200
3 300 2 1 4 5 6
3 2 1 4 5 6
=====================================
四、双向循环链表
class DoubleListNode(object):
"""双向节点类"""
def __init__(self, _item, _prev=None, _next=None):
self.item = _item
self.prev = _prev
self.next = _next
pass
pass
class DoubleCircleLinkedList(SingleCircleLinkedList):
"""双向循环链表类"""
def __init__(self):
self.head = None
def add(self, newdata):
node = DoubleListNode(newdata)
if self.is_empty():
self.head = node
node.prev = node
node.next = node
else:
node.prev = self.head.prev
node.next = self.head
self.head.prev.next = node
self.head.prev = node
self.head = node
def append(self, newdata):
node = DoubleListNode(newdata)
if self.is_empty():
self.head = node
node.prev = node
node.next = node
else:
self.head.prev.next = node
self.head.prev = node
node.prev = self.head.prev
node.next = self.head
def insert(self, pos, newdata):
if pos <= 0:
self.add(newdata)
elif pos > self.length() - 1:
self.append(newdata)
else:
node = DoubleListNode(newdata)
cur = self.head
count = 0
while count < pos - 1:
count += 1
cur = cur.next
node.next = cur.next
cur.next.prev = node
node.prev = cur
cur.next = node
def remove(self, olddata):
if self.is_empty():
return
elif self.head.item == olddata:
if self.length() == 1:
self.head = None
else:
self.head.prev.next = self.head.next
self.head.next.prev = self.head.prev
self.head = self.head.next
else:
cur = self.head.next
while cur != self.head:
if cur.item == olddata:
cur.prev.next = cur.next
cur.next.prev = cur.prev
cur = cur.next
顺序表和链表统称线性表,存储线性数据,那么怎么去利用???
看下一节笔记“栈”。