学习笔记,仅供参考,有错必纠
算法与数据结构–基于python
链表
为啥需要链表
顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以使用起来不是很灵活;而链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。
什么是链表
链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理,链表是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是在每一个结点(数据存储单元)里存放下一个结点的位置信息(即地址)。
单向链表
什么是单向链表
单向链表也叫单链表,是链表中最简单的一种形式;它的每个结点包含两个域,一个信息域(元素域)和一个链接域;这个链接指向链表中的下一个结点,而最后一个结点的链接域则指向一个空值。
- 图示
单链表也可以没有头结点,如果没有头结点的话,那么单链表就会变成这样:
- 关于头结点
头结点是为了操作的统一与方便而设立的,放在第一个元素结点之前,其数据域一般无意义(当然有些情况下也可存放链表的长度、用做监视哨等等),首元结点也就是第一个元素的结点,它是头结点后边的第一个结点,头结点不是链表所必需的。
- 关于头指针
在线性表的链式存储结构中,头指针是指链表指向第一个结点的指针,若链表有头结点,则头指针就是指向链表头结点的指针;头指针具有标识作用,故常用头指针冠以链表的名字;无论链表是否为空,头指针均不为空,头指针是链表的必要元素。
单列表的操作
is_empty()
#链表是否为空
length()
#链表长度
travel()
#遍历整个链表
append(item)
#链表尾部添加元素
add(item)
#链表头部添加元素
insert(pos, item)
#指定位置添加元素
search(item)
#查找结点是否存在
remove(item)
#删除结点
节点的实现
class Node:
def __init__(self, elem):
#初始化数据区
self.elem = elem
#初始化链接区
self.next = None
单链表的实现
首先,我们参照下图中的链表形式,来构造我们的单链表:
同时,我们参照下图中的尾插法,在我们的链表末尾插入数据:
参照下图中的头插法,在链表头部添加元素:
参照下图中的插入法,在链表中指定位置插入元素:
参照下图中的删除法,在链表中删除某个指定元素:
定义单链表:
class SingleLinkedList:
def __init__(self):
#头指针
self.__head = None
#判断链表是否为空
def is_empty(self):
return self.__head is None
#查询长度
def length(self):
#判断是否为空
if self.is_empty():
return 0
else:
#定义游标
cur = self.__head
#计数
count = 0
while cur != None:
cur = cur.next
count += 1
return count
#遍历链表
def travel(self):
if self.is_empty():
return
else:
#定义游标
cur = self.__head
while cur != None:
#打印数据
print(cur.elem, " ")
cur = cur.next
print('')
#链表尾部添加元素
def append(self, item):
#定义新节点
node = Node(item)
#判断链表是否为空
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next!= None:
cur = cur.next
#while循环结束后,cur到达了最后一个节点
cur.next = node
#链表开头插入
def add(self, item):
#新节点
node = Node(item)
if self.is_empty():
self.__head = node
else:
node.next = self.__head
self.__head = node
#链表中指定位置插入
def insert(self, pos, item):
#新节点
if pos < 0:
self.add(item)
elif pos > (self.length() - 1):
self.append(item)
else:
node = Node(item)
pre = self.__head
count = 0
while count < (pos -1):
pre = pre.next
count += 1
#下面的两行代码顺序不可变
node.next = pre.next
pre.next = node
#查找节点
def search(self, item):
if self.is_empty():
return False
else:
cur = self.__head
while cur!= None:
if cur.elem == item:
return True
else:
cur = cur.next
return False
#删除节点
def remove(self,item):
if self.is_empty():
return
else:
# 定义cur游标
cur = self.__head
# 定义pre游标
pre = None
# 查找所有的位置有没有要删除的,若有则删
while cur != None:
# 判断cur指向的数据,是否为要删的数据
if cur.elem == item:
# 考虑特殊情况,恰好要删的是第一个元素
if cur == self.__head:
# 头结点指向后一个结点
self.__head = cur.next
else:
# 删除
pre.next = cur.next
return
else:
# 移动游标,先移动pre,再移动cur
pre = cur
cur = cur.next
测试:
if __name__ == "__main__":
sll = SingleLinkedList()
print(sll.is_empty())
print(sll.length())
print('-'*20)
sll.travel()
print('-'*20)
sll.append(1)
sll.add(2)
sll.append(3)
sll.travel()
print('-'*20)
print(sll.is_empty())
print(sll.length())
print('-'*20)
sll.insert(1,'abc')
sll.travel()
print(sll.search(2))
print('-'*20)
sll.remove('abc')
sll.travel()
输出:
True
0
--------------------
--------------------
2
1
3
--------------------
False
3
--------------------
2
abc
1
3
True
--------------------
2
1
3
链表与顺序表的对比
链表与顺序表各种操作的时间复杂度:
操作 | 链表 | 顺序表 |
---|---|---|
访问元素 | O(n) | O(1) |
在头部插入/删除 | O(1) | O(n) |
在尾部插入/删除 | O(n) | O(1) |
在中间插入/删除 | O(n) | O(n) |