单向链表
单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值。
- 表元素域elem用来存放具体的数据。
- 链接域next用来存放下一个节点的位置(python中的标识)
- 变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点。
节点实现
class Node():
"""结点类"""
def __init__(self,elem):
#存放元素数据
self.elem=elem
#next是下一个节点的标识
self.next=None
单链表的操作
- is_empty() 链表是否为空
- length() 链表长度
- travel() 遍历整个链表
- add(item) 链表头部添加元素
- append(item) 链表尾部添加元素
- insert(pos, item) 指定位置添加元素
- remove(item) 删除节点
- search(item) 查找节点是否存在
单链表的实现
class Single_LinkList():
def __init__(self,node=None):
#头节点定义为私有变量
self._head=node
def is_empty(self):
#判断链表是否为空
if self._head is None:
return True
else:
return False
def length(self):
#返回链表的长度
# cur游标,用来移动遍历节点
# count用来计数
cur=self._head
count = 0
while cur !=None:
count+=1
cur=cur.next
return count
def travel(self):
#遍历整个链表
cur=self._head
while cur!=None:
print(cur.elem,end=' ')
cur=cur.next
print()
头部添加元素
def add(self,item):
#在头部添加一个节点
# 先创建一个保存item值的节点
node=Node(item)
#判断链表是否为空
if self._head==None:
self._head=node
else:
# 将新节点的链接域next指向头节点,即_head指向的位置
node.next=self._head
# 将链表的头_head指向新节点
self._head=node
尾部添加元素
def append(self,item):
#在尾部添加一个节点
node=Node(item)
#若链表为空,直接将该节点作为链表的第一个元素
if self._head==None:
self._head=node
else:
cur = self._head
while cur.next !=None:
cur=cur.next
cur.next=node
指定位置添加元素
def insert(self,pos,item):
#在指定位置pos添加节点
#pos从0开始
# 若指定位置pos为第一个元素之前,则执行头部插入
if pos<=0:
self.add(item)
# 若指定位置超过链表尾部,则执行尾部插入
elif pos>=self.length():
self.append(item)
# 找到指定位置
else:
# pre用来指向指定位置pos的前一个位置pos-1,初始从头节点开始移动到指定位置
pre=self._head
count=0
node = Node(item)
while count<(pos-1):
count+=1
pre=pre.next
# 先将新节点node的next指向插入位置的节点
node.next=pre.next
# 将插入位置的前一个节点的next指向新节点
pre.next=node
删除节点
def remove(self,item):
#删除某一个节点
# 若链表为空,则直接返回
if self.is_empty():
return
pre=None
cur=self._head
while cur!=None:
#若没有找到元素,继续按链表后移节点
if cur.elem!=item:
pre=cur
cur=cur.next
else:
#若要删除的点为头节点
if cur==self._head:
self._head=cur.next
#删完要break,不然陷入死循环
break
else:
#要删除的点不是头节点
pre.next=cur.next
break
查找节点是否存在
def search(self,item):
#查找节点是否存在
cur=self._head
while cur!=None:
if cur.elem==item:
return True
else:
cur = cur.next
return False
测试
if __name__ == '__main__':
single_obj=Single_LinkList()
print(single_obj.is_empty())
print(single_obj.length())
single_obj.append(1)
single_obj.append(2)
single_obj.append(3)
single_obj.append(4)
single_obj.append(5)
single_obj.travel()
single_obj.add(-1)
single_obj.travel()
single_obj.insert(-1,-2)
single_obj.travel()
single_obj.insert(2,0)
single_obj.travel()
print(single_obj.search(0))
single_obj.remove(2)
single_obj.travel()
True
0
1 2 3 4 5
-1 1 2 3 4 5
-2 -1 1 2 3 4 5
-2 -1 0 1 2 3 4 5
True
-2 -1 0 1 3 4 5
链表与顺序表的对比
链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的使用要相对灵活。
链表与顺序表的各种操作复杂度如下所示:
操作 | 链表 | 顺序表 |
---|---|---|
访问元素 | O(n) | O(1) |
在头部插入/删除 | O(1) | O(n) |
在尾部插入/删除 | O(n) | O(1) |
在中间插入/删除 | O(n) | O(n) |
注意虽然表面看起来复杂度都是 O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作。链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是O(1)。顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前后移位操作,只能通过拷贝和覆盖的方法进行。