目 录
1. 链表
1.1 链表概念
前面提到,栈、队列都可以视作是一种特殊的线性表,在绝大多数语言中都是连续存储的。而链表则是不连续的存储方式,自适应内存大小,通过指针的使用,灵活将不同的内存块联系起来,实现动态增加动态删除。
链表的基础是结点 node,每个结点应该包含至少两个区域,一个是存放数据的区域,一个是指向下一个节点的指针。对于整个链表而言,应该有指向头结点的头指针,尾部结点根据链表的不同类型可以指向空(单链表),也可以指向头节点(循环链表)。根据指针的配置,结点可以是单向的,也可以是双向的,从而链表可以是单向的,也可以是双向的。
1.2 链表优点
相对于连续存储的结构, 链表优点还是很明显的:
- 内存空间是不连续的,可以充分利用计算机的零碎内存,实现灵活的内存动态管理。
- 链表不必在创建时就确定大小, 并且大小可以无限的延伸下去(如果计算机内存支持的话)。
- 链表在插入和删除数据时, 只需要移动少量数据,时间复杂度会降低很多。
1.3 链表缺点
链表也有一些缺点:
- 链表如果按照位置访问,访问任何一个位置的元素时, 都需要从头开始访问(无法跳过第一个元素访问任何一个元素)。即无法通过下标直接访问元素。备注:笔者认为,没有完美的数据结构能够满足所有用户需求,无论是栈、队列抑或是链表,都需要结合实际数据特性和用户使用特性来决定数据结构。比如链表的这个缺点,如果数据不需要按照位置访问,可能就不是缺点。
- 因为需要存储指向下一个结点的指针,额外要消耗内存。
2.常见链表结构
2.1 单链表
2.1.1 单链表结构
单链表是最简单的链式存取的数据结构,每个单独数据是以对应的结点来表示的,每个结点由元素和指针构成。
2.1.2 单链表代码实现
单向链表需要实现的基本操作有:
- 创建链表
- 新增结点到链表尾部
- 删除第n个结点
- 读取第n个结点
- 逆转顺序读取链表(非必需)
每个人有每个人的设计代码实现,有些同学在单链表中只设计一个头指针,需要在链表尾部新增数据时候从头指针一直查找到最后,而我喜欢创建头指针、尾指针两个指针,以空间换时间,代码实现也更简单。
下面是我的代码实现:
class Node():
def __init__(self,value):
self.val=value
self.next=None
class SingleLink():
def __init__(self):
self.phead=None
self.ptail=None
self.length=0
def add(self,value):
node=Node(value)
if(not self.phead):
self.phead=node
self.ptail=node
self.length=1
else:
self.ptail.next=node
self.ptail=node
self.ptail.next=None
self.length+=1
#删除第 n 个结点
def delete(self,n):
#如果当前链表为空,不操作
if(self.length==0):
return(None)
# 如果超出现有长度,删除最后一个结点
if(n>self.length):
n=self.length
# 如果删除第一个结点,即头节点
# 则直接移动头指针到下一个结点
if(n==1):
self.phead=self.phead.next
self.length-=1
return(None)
# 获得头指针
p=self.phead
# 目的是获得被删除结点的前一个结点
while(n>2):
p=p.next
n-=1
# 如果这是最后一个结点,那么前一个结点直接指向空
# 如果这不是最后一个结点,那么前一个结点直接指向再下一个结点
if(p.next.next):
p.next=p.next.next
else:
p.next=None
self.length-=1
def read(self,n):
if(n>self.length):
n=self.length
p=self.phead
while(n>1):
p=p.next
n-=1
return(p.val)
def show(self):
print("current length is {0}".format(self.length))
p=self.phead
while( p.next ):
print(p.val,end=" , ")
p=p.next
print(p.val)
return(None)
def reverse_show(self,node):
p=node
if(p.next):
self.reverse_show(p.next)
print(p.val,end=" , ")
运行相关测试代码后结果如下:
#创建单链表
a_link=SingleLink()
#填充数据 1~9
for i in range(1,10):
a_link.add(i)
print()
#展示数据
print("*"*30+"填充数据 1~9之后链表为"+"*"*30)
a_link.show()
#逆序展示数据
print("*"*30+"逆序展示链表数据"+"*"*30)
a_link.reverse_show(a_link.phead)
print()
#连续两次删除头节点
print("*"*50)
print("Now to delete first node twice")
a_link.delete(1)
a_link.show()
a_link.delete(1)
a_link.show()
print("*"*50)
#连续两次删除尾节点
print("Now to delete last node twice")
a_link.delete(7)
a_link.show()
a_link.delete(6)
a_link.show()
print("*"*50)
#删除中间结点第二个结点
print("Now to delete second node ")
a_link.delete(2)
a_link.show()
print("*"*50)
2.2 单向循环链表
2.2.1 单向循环链表结构
单向循环链表即将单链表的尾结点指向头结点的链表,又称之为循环单链表(Circular linkedlist)。
2.2.2 单循环链表代码实现
单循环链表需要实现的基本操作有:
- 创建链表
- 新增结点到链表尾部
- 新增结点到链表第 n 个结点
- 删除第n个结点
- 读取第n个结点
同样,在单循环链表中,如果设计头指针、尾指针两个指针,则不光是空间换时间,而且代码实现会更方便。
下面是我的代码实现:
class Node():
def __init__(self,value):
self.val=value
self.next=None
class S_CirLink():
def __init__(self):
self.phead=None
self.ptail=None
self.length=0
def addTail(self,value):
node=Node(value)
if(not self.phead):
self.phead=node
self.ptail=node
node.next=node
self.length=1
else:
self.ptail.next=node
self.ptail=node
node.next=self.phead
self.length+=1
# n 限制在大于等于 1
def addN(self,value,n):
node=Node(value)
if(not self.phead):
self.phead=node
self.ptail=node
node.next=node
self.length=1
else:
if(n==1):
node.next=self.phead
self.phead=node
self.ptail.next=self.phead
self.length+=1
return(None)
if(n>self.length):
n=self.length+1
p=self.phead
while(n>2):
p=p.next
n-=1
node.next=p.next
p.next=node
self.length+=1
return(None)
#删除第 n 个结点
def delete(self,n):
#如果当前链表为空,不操作
if(self.length==0):
return(None)
# 如果超出现有长度,删除最后一个结点
if(n>self.length):
n=self.length
# 如果删除第一个结点,即头节点
# 则直接移动头指针到下一个结点
if(n==1):
if(self.length==1):
self.phead=None
self.ptail=None
self.length=0
return(None)
self.phead=self.phead.next
self.ptail.next=self.phead
self.length-=1
return(None)
# 此处处理链表长度大于1 的情况,获得头指针
p=self.phead
# 目的是获得被删除结点的前一个结点
while(n>2):
p=p.next
n-=1
# 因为是循环链表,所以链表长度大于1时候删除一个结点,
# 链表肯定不为空,直接指向下一个链表即可
if(p.next!=self.ptail):
p.next=p.next.next
else:
p.next=p.next.next
self.ptail=p
self.length-=1
def read(self,n):
if(n>self.length):
n=self.length
p=self.phead
while(n>1):
p=p.next
n-=1
return(p.val)
def show(self):
print("current length is {0}".format(self.length))
p=self.phead
while( p.next!=self.phead ):
print(p.val,end=" , ")
p=p.next
print(p.val)
return(None)
测试代码如下:
#创建单向循环链表
a_link=S_CirLink()
#填充数据 1~9
for i in range(1,10):
a_link.addTail(i)
print()
#展示数据
print("*"*30+"填充数据 1~9之后链表为"+"*"*30)
a_link.show()
#连续两次删除头节点
print("*"*50)
print("Now to delete first node twice")
a_link.delete(1)
a_link.show()
a_link.delete(1)
a_link.show()
print("*"*50)
#删除中间结点第二个结点
print("Now to delete second node ")
a_link.delete(2)
a_link.show()
print("*"*50)
#连续两次删除尾节点
print("Now to delete last node twice")
a_link.delete(7)
a_link.show()
a_link.delete(6)
a_link.show()
print("*"*50)
# 增加一个结点,排序为 3
print("Now to add new node in third place,the value is 11")
a_link.addN(11,3)
a_link.show()
# 增加一个尾结点,排序为 6
print("Now to add new node in 6th place,the value is 12")
a_link.addN(12,6)
a_link.show()
运行结果:
2.3 双向链表
2.3.1 双向链表结构
双向链表是结点 Node 既有一个 prev (前驱指针) ,又有一个 next (后驱指针) 来构成的一种数据结构。
2.3.2 双向链表代码实现
双向链表需要实现的基本操作有:
- 创建双向链表
- 新增结点到链表尾部
- 新增结点到链表第 n 个结点
- 删除第n个结点
- 读取第n个结点
下面是我的代码实现:
class Node():
def __init__(self,value):
self.val=value
self.prev=None
self.next=None
class Bid_Link():
def __init__(self):
self.phead=None
self.ptail=None
self.length=0
def addTail(self,value):
node=Node(value)
if(not self.phead):
self.phead=node
self.ptail=node
self.length=1
else:
self.ptail.next=node
node.prev=self.ptail
self.ptail=node
node.next=None
self.length+=1
def addN(self,value,n):
if(not self.phead or n>self.length):
self.addTail(value)
return(None)
node=Node(value)
if(n==1):
node.next=self.phead
self.phead.prev=node
self.phead=node
self.length+=1
return(None)
p=self.phead
while(n>2):
p=p.next
n-=1
node.prev=p
node.next=p.next
node.next.prev=node
p.next=node
self.length+=1
return(None)
#删除第 n 个结点,n 大于等于 0
def delete(self,n):
#如果当前链表为空,不操作
if(self.length==0):
return(None)
# 如果超出现有长度,删除最后一个结点
if(n>self.length):
n=self.length
# 如果删除第一个结点,即头节点
# 则直接移动头指针到下一个结点
if(n==1):
if(self.length==1):
self.phead=None
self.ptail=None
self.length=0
return(None)
self.phead=self.phead.next
self.phead.prev=None
self.length-=1
return(None)
# 此处处理链表长度大于1 的情况,获得头指针
p=self.phead
# 目的是获得被删除结点的前一个结点
while(n>2):
p=p.next
n-=1
# 双向链表,需要处理前驱指针、后驱指针
tmp=p.next
if(p.next.next):
p.next=tmp.next
tmp.next.prev=p
else:
p.next=None
self.ptail=p
self.length-=1
return(None)
def read(self,n):
if(n>self.length):
n=self.length
p=self.phead
while(n>1):
p=p.next
n-=1
return(p.val)
def show(self):
print("current length is {0}".format(self.length))
p=self.phead
while(p.next):
print(p.val,end=" , ")
p=p.next
print(p.val)
return(None)
测试代码如下:
#创建双向循环链表
a_link=Bid_Link()
#填充数据 1~9
for i in range(1,10):
a_link.addTail(i)
print()
#展示数据
print("*"*30+"填充数据 1~9之后链表为"+"*"*30)
a_link.show()
#连续两次删除头节点
print("*"*50)
print("Now to delete first node twice")
a_link.delete(1)
a_link.show()
a_link.delete(1)
a_link.show()
print("*"*50)
#删除中间结点第二个结点
print("Now to delete second node ")
a_link.delete(2)
a_link.show()
print("*"*50)
#连续两次删除尾节点
print("Now to delete last node twice")
a_link.delete(7)
a_link.show()
a_link.delete(6)
a_link.show()
print("*"*50)
# 增加一个结点,排序为 3
print("Now to add new node in third place,the value is 11")
a_link.addN(11,3)
a_link.show()
# 增加一个尾结点,排序为 6
print("Now to add new node in 6th place,the value is 12")
a_link.addN(12,6)
a_link.show()
运行结果:
文章评论