在c++中,我们以指针的形式直接访问数据的地址,在这些较为现代的预言中,单链表结构中的一个节点,包含了一个数据项和指针值。一个特殊的空值(或nil),表示指针值是一个空链接。我们并不使用数组来构建不连续的内存,而是直接向计算机请求一个指针指向一个新的节点,这个节点来自于名为对象堆的一个内建的不连续内存区域。然后,我们在该节点中设置了指向另一个节点的指针,由此建立了到结构中的其他数据的一个链接。然而,c++中得通过一种特殊的处理或删除操作,来向其返回那些不再使用的节点。
python程序员使用对 对象的引用建立起了节点和链表结构。在Python中,任何变量都可以引用任何内容,包括None值,它意味着一个空的链接。由此。Python程序员通过定义包含两个字段的一个对象,从而定义了一个单链表节点。这两个字段是:数据项的一个引用和到另一个节点的一个引用。Python为每一个新的节点对象提供了动态分配的非连续的内存,并且当对象不再被引用程序引用的时候,会自动把内存返回给系统(垃圾收集)。
定义一个单链表节点类。
如下是一个简单的、单链表节点类的代码,代码放在模块node.py中。
"""
File: node.py
定义一个单链表节点类
"""
class Node(object):
def __init__(self,data,next = None):
self.data = data
self.next = next
创建一个单链表结构,并且访问输出其内容:
"""
File: testnode.py
测试节点类。
"""
from node import Node
head = None
#创建一个单链表结构
for count in range(1,6):
head = Node(count,head)
#输出节点内容,输出完后,单链表结构也销毁了
while head != None:
print(head.data) #输出:5, 4, 3, 2, 1
head = head.next
单链表结构创建好后其结构如下图:
head为头指针,指向第一个节点对象(head为变量,是第一个节点对象的引用),最后一个节点对象的字段值next为None值。
单链表结构上的操作
1. 遍历
遍历操作需要一个临时变量,这个变量先初始化为链表结构的head指针,然后控制一个循环,如下所示:
from node import Node
head = None
#创建一个单链表结构
for count in range(1,6):
head = Node(count,head)
probe = head
#输出节点内容,输出完后,单链表结构依然存在
while probe != None:
print(probe.data) #输出:5, 4, 3, 2, 1
probe = probe.next
通常,遍历一个单链表结构会访问每一个节点,并且当遇到一个空链接的时候终止。因此,值None充当负责停止这个过程的哨兵。
遍历在时间上是线性的,并且不需要额外的内存。
2. 搜索
在这个例子中,会出现两个可能的哨兵:
空链接,表明不再有要检查的数据。
等于目标项的一个数据项,表明一次成功的搜索。
如下是搜索一个给定项的代码:
from node import Node
head = None
#创建一个单链表结构
for count in range(1,6):
head = Node(count,head)
probe = head
targetItem = 3 #搜索链表结构中是否存在数据项为3的节点
while probe != None and targetItem != probe.data:
probe = probe.next
if probe != None:
print("目标数据项targetItem已搜索到")
else:
print("目标数据项targetItem不存在于当前链表结构中")
若 nn 是链表结构中节点数目,访问第 ii 项的代码如下,其中 0 <= i < n。
from node import Node
head = None
#创建一个单链表结构
for count in range(1,6):
head = Node(count,head)
probe = head
index = 2 #第2个数据项,数据项从0开始
while index > 0:
probe = probe.next
index -= 1
print(probe.data) #输出3
3. 替换
替换一个给定的项,代码如下:
# -*- coding: utf-8 -*-
"""
Created on Sun Jul 1 20:44:10 2018
@author: aoanng
"""
from node import Node
head = None
#创建一个单链表结构
for count in range(1,6):
head = Node(count,head)
probe = head
targetItem = 3 #搜索链表结构中是否存在数据项为3的节点,并替换
newItem = 20 #替换搜索到的目标项
while probe != None and targetItem != probe.data:
probe = probe.next
if probe != None:
probe.data = newItem
print(probe.data) #输出20
else:
print("该链表结构中不存在目标项targetItem")
还有一个替换第 ii 项的操作,它假设 0 <= i < n。其代码如下:
from node import Node
head = None
#创建一个单链表结构
for count in range(1,6):
head = Node(count,head)
probe = head
index = 3 #搜索链表结构中第3个数据项,并替换
newItem = 20 #替换搜索到的目标项
while index > 0:
probe = probe.next
index -= 1
probe.data = newItem
print(probe.data) #20
4. 插入
1、在开始处插入
代码如下:
from node import Node
head = None
#创建一个单链表结构
for count in range(1,6):
head = Node(count,head)
newItem = 20
head = Node(newItem,head) #在开始处插入
2、在末尾插入
对于单链表结构来说,在末尾插入一项必须考虑如下两种情况:
head指针为None,此时,将head指针设置为新的节点
head指针不为None,此时,代码将搜索最后一个节点,并将其next指针指向新的节点。
from node import Node
head = None
#创建一个单链表结构
for count in range(1,6):
head = Node(count,head)
newItem = 20
newNode = Node(newItem) #节点
if head is None:
head = newNode
else:
probe = head
while probe.next != None:
probe = probe.next
probe.next = newNode
3、在任何位置插入
from node import Node
head = None
#创建一个单链表结构
for count in range(1,6):
head = Node(count,head)
newItem = 20
index = 3 #在第3个节点插入,从0开始排序
#如果链表为空或者index小于等于0,即在链表头部插入
if head is None or index <= 0:
head = Node(newItem,head)
else:
probe = head
#不能超过链表结构的长度
while index > 1 and probe.next != None:
probe = probe.next
index -= 1
probe.next = Node(newItem,probe.next)
插入之后单链表结构如下图所示:
5. 删除
1、在开始处删除
通常假设链表结构中至少有一个节点。其代码如下:
from node import Node
head = None
#创建一个单链表结构
for count in range(1,6):
head = Node(count,head)
removeItem = head.data
head = head.next # 在开始处删除
print(removeItem) # 5
print(head.data) # 4
2、从末尾删除
只有一个节点。head指针设置为None
在最后一个节点之前没有节点。代码搜索倒数第2个节点并将其next指针设置为None。
from node import Node
head = None
#创建一个单链表结构
for count in range(1,6):
head = Node(count,head)
removeItem = head.data
if head.next is None:
head = None
else:
probe = head
while probe.next.next != None:
probe = probe.next
removeItem = probe.next.data
probe.next = None
print(removeItem) # 1
3、 在任意位置删除
i <= 0———-使用删除第一项的代码
0 < i < n——-搜索位于i-1位置的节点,删除其后面的节点
i>=n————删除最后一个节点
from node import Node
head = None
#创建一个单链表结构
for count in range(1,6):
head = Node(count,head)
index = 2 # 待删除的项
if index <= 0 or head.next is None:
removeItem = head.data
head = head.next
print(removeItem)
else:
probe = head
while index > 1 and probe.next.next != None:
probe = probe.next
index -= 1
removeItem = probe.next.data
probe.next = probe.next.next # None
print(removeItem) # 3
6. 复杂度权衡:时间、空间和单链表结构
操作
运行时间
在第 i 个位置访问
O(n),平均情况
在第 i 个位置替换
O(n),平均情况
从开始处插入
O(1),最好情况和最差情况
从开始处删除
O(1),最好情况和最差情况
在第 i 个位置插入
O(n),平均情况
在第 i 个位置删除
O(n),平均情况
循环链表
在这个实现中,至少总是有一个节点。这个节点也就是哑头节点,它不包含数据,但是充当了链表结构的开头和结尾的一个标记。其结构如下图所示:
对第 i 个节点的额搜索,从哑头节点之后的节点开始。假设这个空的链表的最初结构如下所示:
from node import Node
#创建一个空循环链表
head = Node(None,None)
head.next = head
如下是在第 i 个位置插入节点的代码,它使用了这个链表结构的新的表示:
from node import Node
#创建一个空循环链表,head指向哑头节点
head = Node(None,None)
head.next = head
probe = head
index = 10
newItem = 20
while index > 0 and probe.next != head:
probe = probe.next
index -= 1
probe.next = Node(newItem,probe.next)
这种实现的优点在于,插入和删除操作只需要考虑一种情况,即第 i 个节点位于当前的第 i 个节点和它的前一个节点之间的这种情况。
双链表结构
双链表结构比单链表结构更有优越性。它允许用户做如下的事情:
从给定的节点,向左移动到前一个节点。
直接移动到最后一个节点。
下图展示了包含3个节点的一个双向链表结构。注意,每个节点中有两个指针,通常称为next和previous。还要注意,有一个外部的tail指针,它允许直接访问结构中的最后一个节点。
双链表结构的节点类的python实现,通过给previous指针添加一个字段,扩展了前面所讨论的Node类。如下是这两个类的代码:
"""
File: node.py
定义单链表和双链表类
"""
class Node(object):
def __init__(self,data,next = None):
self.data = data
self.next = next
class TwoWayNode(Node):
def __init__(self,data,previous = None, next = None):
Node.__init__(self,data,next)
self.previous = previous
如下是测试程序,它通过在末尾添加项来创建一个双链表结构。然后,程序从最后一项开始朝着第一项处理,最终显示了整个链表结构的内容:
"""
File: testtwowaynode.py
测试双链表类
"""
from node import TwoWayNode
#创建一个节点的双链表结构
head = TwoWayNode(1)
tail = head
# 在之前创建的链表结构后面添加4个节点
for data in range(2,6):
tail.next = TwoWayNode(data,tail)
tail = tail.next
probe = tail
while probe != None:
print(probe.data) #5,4,3,2,1
probe = probe.previous
参考:
本篇博客整理于 《数据结构(python语言描述)》