单向链表
单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值。

结点 实现代码(Python):
class Node(object):
"""结点"""
def __init__(self, elem):
self.elem = elem
self.next = None
其中:对象属性 item 用于存放数据元素;对象属性 next 是指向下一个结点的标识

单链表 实现代码(Python):
class SingleLinkList(object):
"""单链表"""
def __init__(self, node=None):
self.__head = node # 外部不要管,不对外暴露,做成私有
is_empty()判断链表是否为空


实现代码(Python):
class SingleLinkList(object):
"""单链表"""
def __init__(self, node=None):
self.__head = node # 外部不要管,不对外暴露,做成私有
def is_empty(self):
"""判断链表是否为空"""
return self.__head is None
length()判断链表长度
对于下面的这样一个链表,我们计算它的length

为了后面更好的解释,我们把上面的图,修改下:

应该从头到尾遍历,
因此,引入一个cur游标变量,记录下当前结点是哪个;引入count变量,用于计算元素个数:




实现代码(Python):
class SingleLinkList(object):
"""单链表"""
def __init__(self, node=None): # node=None 是防止出现用户啥也不传入
self.__head = node # 外部不要管,不对外暴露,做成私有
def length(self):
"""链表长度"""
# cur初始时指向头节点,cur游标,用来移动遍历节点
cur = self.__head
# count 记录数量
count = 0
# 尾节点指向None,当未到达尾部时
while cur is not None:
count += 1
# 将cur后移一个节点
cur = cur.next
return count
遍历链表
和上面判断链表长度的代码相似,就是遍历时候,我们不需要进行count计算操作
实现代码(Python):
class SingleLinkList(object):
"""单链表"""
def __init__(self, node=None):
self.__head = node # 外部不要管,不对外暴露,做成私有
def travel(self):
"""遍历链表"""
cur = self.__head
while cur is not None:
print(cur.elem)
cur = cur.next
链表元素插入
尾插法

这里尾插法需要我们使用cur游标,遍历到最后一个元素,然后从尾部添加元素

实现代码(Python):
class SingleLinkList(object):
"""单链表"""
def __init__(self, node=None):
self._head = node # 外部不要管,不对外暴露,做成私有
def append(self, item):
"""链表尾部插入元素"""
node = Node(item)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next is not None:
cur = cur.next
cur.next = node
头插法

注意:顺序不能乱!
class SingleLinkList(object):
"""单链表"""
def __init__(self, node=None):
self.__head = node # 外部不要管,不对外暴露,做成私有
def add(self, item):
"""链表头部插入,头插法"""
node = Node(item)
node.next = self.__head
self.__head = node
阶段性测试
class Node(object):
"""节点"""
def __init__(self, elem):
self.elem = elem
self.next = None
class SingleLinkList(object):
"""单链表"""
def __init__(self, node=None):
self.__head = node # 外部不要管,不对外暴露,做成私有
def is_empty(self):
"""判断链表是否为空"""
return self.__head is None
def length(self):
"""链表长度"""
# cur初始时指向头节点,cur游标,用来移动遍历节点
cur = self.__head
# count 记录数量
count = 0
# 尾节点指向None,当未到达尾部时
while cur is not None:
count += 1
# 将cur后移一个节点
cur = cur.next
return count
def travel(self):
"""遍历链表"""
cur = self.__head
while cur is not None:
print(cur.elem, end=" ")
cur = cur.next
def append(self, item):
"""链表尾部插入元素"""
node = Node(item)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next is not None:
cur = cur.next
cur.next = node
def add(self, item):
"""链表头部插入,头插法"""
node = Node(item)
node.next = self.__head
self.__head = node
if __name__ == '__main__':
"""测试"""
ll = SingleLinkList()
print(ll.is_empty())
print(ll.length())
ll.append(1)
print(ll.is_empty())
print(ll.length())
ll.append(2)
ll.add(8)
ll.append(3)
ll.append(4)
ll.append(5)
ll.append(6)
ll.travel()
运行输出:
True
0
False
1
8 1 2 3 4 5 6
指定位置添加元素
假设我们要在20与300之间添加一个元素,即 insert(2,6300)
我们需要引入一个pre变量来辅助按操作

class SingleLinkList(object):
"""单链表"""
def __init__(self, node=None):
self.__head = node # 外部不要管,不对外暴露,做成私有
def insert(self, pos, item):
"""
指定位置添加元素
:param pos: 起始从0开始
:param item: 待插入的数值
:return:
"""
# 处理特殊情况
if pos <= 0:
self.add(item) # 头插法
elif pos > (self.length() - 1):
self.append(item) # 尾插法
else:
pre = self.__head
count = 0
while count < (pos - 1):
count += 1
pre = pre.next
# 当循环推出后, pre 指向 pos-1
node = Node(item)
node.next = pre.next
pre.next = node
查找元素是否存在

class SingleLinkList(object):
"""单链表"""
def __init__(self, node=None):
self.__head = node # 外部不要管,不对外暴露,做成私有
def search(self, item):
"""查找元素是否存在"""
cur = self.__head
# 特殊情况,空链表
# cur = self.__head = None
while cur is not None:
if cur.elem == item:
return cur.elem
else:
cur = cur.next
return False
删除元素


class SingleLinkList(object):
"""单链表"""
def __init__(self, node=None):
self.__head = node # 外部不要管,不对外暴露,做成私有
def remove(self, item):
"""删除元素"""
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
else:
pre = cur
cur = cur.next
完整代码(Python)
class Node(object):
"""节点"""
def __init__(self, elem):
self.elem = elem
self.next = None
class SingleLinkList(object):
"""单链表"""
def __init__(self, node=None):
self.__head = node # 外部不要管,不对外暴露,做成私有
def is_empty(self):
"""判断链表是否为空"""
return self.__head is None
def length(self):
"""链表长度"""
# cur初始时指向头节点,cur游标,用来移动遍历节点
cur = self.__head
# count 记录数量
count = 0
# 尾节点指向None,当未到达尾部时
while cur is not None:
count += 1
# 将cur后移一个节点
cur = cur.next
return count
def travel(self):
"""遍历链表"""
cur = self.__head
while cur is not None:
print(cur.elem, end=" ")
cur = cur.next
print("\n")
def append(self, item):
"""链表尾部插入元素"""
node = Node(item)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next is not None:
cur = cur.next
cur.next = node
def add(self, item):
"""链表头部插入,头插法"""
node = Node(item)
node.next = self.__head
self.__head = node
def insert(self, pos, item):
"""
指定位置添加元素
:param pos: 起始从0开始
:param item: 待插入的数值
:return:
"""
# 处理特殊情况
if pos <= 0:
self.add(item) # 头插法
elif pos > (self.length() - 1):
self.append(item) # 尾插法
else:
pre = self.__head
count = 0
while count < (pos - 1):
count += 1
pre = pre.next
# 当循环推出后, pre 指向 pos-1
node = Node(item)
node.next = pre.next
pre.next = node
def search(self, item):
"""查找元素是否存在"""
cur = self.__head
# 特殊情况,空链表
# cur = self.__head = None
while cur is not None:
if cur.elem == item:
return cur.elem
else:
cur = cur.next
return False
def remove(self, item):
"""删除元素"""
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
if __name__ == '__main__':
"""测试"""
ll = SingleLinkList()
print(ll.is_empty())
print(ll.length())
ll.append(1)
print(ll.is_empty())
print(ll.length())
ll.append(2)
ll.add(8)
ll.append(3)
ll.append(4)
ll.append(5)
ll.append(6)
ll.insert(-1, 9)
ll.insert(2, 6300)
ll.insert(12, 200)
ll.travel() # 9 8 6300 1 2 3 4 5 6 200
ll.append(1)
ll.travel() # 9 8 6300 1 2 3 4 5 6 200 1
ll.remove(1)
ll.travel() # 9 8 6300 2 3 4 5 6 200 1
ll.remove(6300)
ll.travel() # 9 8 2 3 4 5 6 200 1
运行输出:
True
0
False
1
9 8 6300 1 2 3 4 5 6 200
9 8 6300 1 2 3 4 5 6 200 1
9 8 6300 2 3 4 5 6 200 1
9 8 2 3 4 5 6 200 1
链表与顺序表的对比
链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的使用要相对灵活。
链表与顺序表的各种操作复杂度如下所示:
| 操作 | 链表 | 顺序表 |
|---|---|---|
| 访问元素 | O(n) | O(1) |
| 在头部插入/删除 | O(1) | O(n) |
| 在尾部插入/删除 | O(n) | O(1) |
| 在中间插入/删除 | O(n) (时间花在遍历上) | O(n) (时间花在数据搬迁上) |
注意虽然表面看起来复杂度都是 O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作。链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是O(1)。顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前后移位操作,只能通过拷贝和覆盖的方法进行。

浙公网安备 33010602011771号