在数组之后,链表结构(linkedstructure)可能是程序中最常用的数据结构。链表结构中最简单的是单链表结构(singly linked structure)和双链表结构(doubly linkedstructure)。为了形象的表示链表结构,我们采用格子和指针来表示链表结构。
1 单链表结构
单链表结构示意图:
单链表结构的用户,沿着一个外部的头链接(head link)来访问第一项。然后用户通过从第一项产生的、串联起来的、单个的链条来访问其他的项。因此,单链表结构和容易获取一个项的后继项,但并不是很容易获取一个项的前驱项。最后一项没有指向下一项的链接,叫做空链接(empty link)。
链表结构的节点包含了一个数据项以及到结构中的下一个节点的一个链接。Python程序员使用对对象的引用建立起了节点和链表的结构。head=Node(item, head)这种链接结构是从后向前创建的,即新加入的值在最前端。
链表结构的插入和删除操作和数组的插入删除操作不同点在于:
(1)一旦找到插入点和删除点,就可以进行插入和删除,而不需要在内存中移动数据项;
(2)在每一次的插入和删除的过程中,链表结构会调整大小,并且不需要额外的内存代价,也不需要复制数据项。
单链表结构相对于数组的主要优点并不是时间性能,而是内存性能。当必须调整数组大小的时候,其时间和内存都是线性的,当调整链表结构的大小的时候,其时间和内存都是常数。在链表中没有浪费内存的问题。但是链表结构确实有额外的内存代价,因为链表结构需要为直接分配内存单元格。
如果试图访问一个None节点,那么python则会抛出一个AttributeError作为相应,为了保证不会发生意外,在尝试访问前,我们先询问其是否为None。
单链表的操作:
1.遍历:遍历的时候要使用一个临时的指针变量,这个指针变量先初始化为链表结构的head指针,然后控制一个循环。如果不使用临时指针变量进行遍历的话,就会把这个链表所有的值删除了,因为Head节点控制的是链表的头节点,当head节点移动时,说有起始点发生了改变。
2.搜索:搜索终止的可能性分为两种:第一种是找到了目标点,第二种是遍历整个链表也没有找到目标点。
3.替换:在链表结构中搜索一个给定的项或给定的位置。如果是替换给定的项,那么就要判断该项是否在链表中,如果在则替换且返回True,如果不在则不能替换返回False;如果是给定的位置,我们假设给定位置的数值在链表的范围内,即假设链表的长度为n,其给定位置的值为0 <= index < n,这种情况就直接遍历到指定位置,然后完成替换即可。
4.插入:插入的情况根据插入位置index分为三种:插在开头,插在中间,插在结尾。而根据节点的情况可以分为两种:空节点、非空节点。综合这两个变量可知:当插入位置索引值index <= 0或者节点为空的时候,都要插在开头,进行head=Node(item, head)。接下来就遍历链表,直至到达索引值位置处或者链表末尾才结束遍历,然后进行插值操作:probe.next = Node(newItem, probe.next)。当我们访问到最后一个节点时,probe.next= None。
5.删除:删除和插入的分析思路是一样,当index <=0 或者head.next is None 时删除的第一个元素。删除中间节点以及结尾节点都需找到对应的索引值的前边的那个节点,所以要index > 1 and probe.next.next != None,直到找到满足条件的节点,删除其后边节点,将所有节点链接起来:probe.next = probe.next.next
Code:
class Node( object ):"""representsa singly linked node"""
def __init__ ( self , data , next= None ):
self .data = data
self .next = next
def main ():
head = None # head link
# set value to Node
for i in range ( 6 ):
head = Node(i , head)
# traversal
print ( "##############Traversal ##############" )
probe = head
while probe != None :
print (probe.data)
probe = probe.next
# search
print ( "############## Search##############" )
targetValue = 4
probe = head
count = 0
while probe != None and probe.data != targetValue:
probe = probe.next
count += 1
if probe != None :
print ( "TargetValue has beenfound, it is located in " , count)
else :
print ( "TargetVaule is not inthe linked structure" )
# replace
print ( "##############Replace ##############" )
targetItem = 9
newItem = 10
targetIndex = 3
probe = head
# given target item
while probe != None and probe.data != targetItem:
probe = probe.next
if probe != None :
probe.data = newItem
print ( "True" )
# return True
else :
print ( "False" )
# return False
# given target index
probe = head
while targetIndex > 0 :
probe = probe.next
targetIndex -= 1
probe.data = newItem
# Insert
print ( "############## Insert##############" )
targetIndex = 2
newItem = 20
if targetIndex <= 0 or head is None :
head = Node(newItem , head)
else :
probe = head
while targetIndex > 1 and probe.next != None :
probe = probe.next
targetIndex -= 1
probe.next = Node(newItem , probe.next)
# traversal
print ( "############## Insertresult ##############" )
probe = head
while probe != None :
print (probe.data)
probe = probe.next
print ( "############## Delete##############" )
targetIndex = 1
if targetIndex <= 0 or head.next is None :
removedItem = head.data
head = head.next
print (removedItem)
# return removedItem
else :
probe = head
while targetIndex > 1 and probe.next.next != None :
probe = probe.next
targetIndex -= 1
removedItem =probe.next.data
probe.next = probe.next.next
print (removedItem)
# return removedItem
if __name__ == "__main__" :
main()
2 双链表结构
双链表结构示意图:
双链表包含了两个方向的链接,用户很容易移动到一个项的后继项和它的前驱项,第二个外部链接叫尾链接(tail link),它允许双链表结构的用户直接访问最后一项。但双链表结构中的额外指针需要额外的、线性的内存使用量。
Code:
# create a doubly linked structure with one node head = TwoWayNode(1) tail = head # Add four nodes to the end of the doubly linked structure for data in range(2, 6): tail.next = TwoWayNode(data, tail) tail = tail.next # print the contents of the linked structure in reverse order probe = tail while probe != None: print(probe.data) probe = probe.previous
3 带有一个哑头节点的循环链表结构
循环链表结构包含了从结构中的最后一个节点返回到第一个节点的一个链接。哑头节点(dummy header node)不包含数据,但是充当了链表结构的开头和结尾的一个标记。下边分别展示带有哑头节点的一个空循环链表结构和在插入了第1个节点之后的循环链表结构。
这种结构的优点在于插入和删除操作只需要考虑一种情况,即第i个节点位于当前的第i个节点和它的前一个节点之间的这种情况。
Code :
# 带一个哑头节点的循环链表
head = Node(None, None)
head.next = head
# 插入数值
probe = head
while index > 0 and probe.next != head:
probe= probe.next
index-= 1
probe.next = Node(item, probe.next)
# 如果想循环放入数值
head = Node(None, None) head.next = head for i in range(10): head.next = Node(i, head.next) head = head.next head= head.next probe = head.next while probe != head: print(probe.data) probe = probe.next