单链表
- 为什么需要单链表?
顺序表需要预先知道数据的大小来申请连续的存储空间,其在进行扩展的时候需要进行数据的搬迁,灵活性教差。
单链表可以充分利用计算机内存空间,实现灵活的内存动态管理
链表的定义
链表是一种常见的基础数据结构,是一种线性表,不像顺序表是连续存储数据的,单链表是在每一个节点(数据存储单元)里存放下一个节点的位置信息(即地址)。
单项链表
单项列表也叫单链表,其结构如上
实现一个单链表
我们抛出python中的内置函数&方法,我们自己写一个类,来实现上图中的单向链表,并实现以下功能:
- is_empty() 链表是否为空
- length() 链表长度
- travel() 遍历整个链表
- append(item) 链表尾部添加元素
- add(item) 链表头部添加元素
- insert(pos, item) 指定位置添加元素
- search(item) 查找节点是否存在
- remove(item) 节点删除
先实现一个节点
class SingleLinkNode(object):
# 单链表节点
def __init__(self, item):
# 用来存储数据
self.item = item
# 存储下一个节点数据的地址
self.next = None
单链表的实现
结合上图实现is_empty()
class SingleLinkList(object):
'''单链表'''
# 空链表
# 非空链表
def __init__(self, node = None):
# __head是头结点,如果该链表为空,他就是空的,如果非空,就是传入的
self.__head = node
def is_empty(self):
'''判断是否为空'''
# if self.__head == None:
# return True
# else:
# return False
# 上面4行代码可以用下面一行表示
return self.__head == None
结合上图实现length()&travel()方法
def length(self):
'''链表的长度'''
# cur初始指向节点头
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.item, end=' ')
cur = cur.next
print(' ')
尾部添加append()
def append(self, item):
'''尾部添加'''
# 先创建一个新的节点
node = SingleLinkNode(item)
# 如果是一个空节点,让__head指向新节点
if self.is_empty():
self.__head = node
# 如果非空,利用游标遍历,当游标的下一个节点为空时,当前游标所指向的节点指向新节点
else:
cur = self.__head
while cur.next != None:
cur = cur.next
cur.next = node
头部添加add()
def add(self, item):
'''头部添加'''
node = SingleLinkNode(item)
# 让新节点指向头部节点,即__head指向的节点
node.next = self.__head
# 再让__head指向新节点
self.__head = node
在指定位置添加元素(插入)insert()
def insert(self, pos, item):
'''指定位置添加元素'''
# 如果小于等于0,头部添加
if pos <= 0:
self.add(item)
# 如果大于0,尾部添加
elif pos > self.length():
self.append(item)
# 找到指定位置
else:
node = SingleLinkNode(item)
cur = self.__head
count = 0
while count < (pos-1):
count += 1
cur = cur.next
# 先将新节点指向插入节点
node.next = cur.next
# 再将插入位置的前一个节点指向新节点
cur.next = node
查询search()&删除remove()
def search(self, item):
'''查询某个值在链表中是否存在'''
cur = self.__head
while cur != None:
if cur.item == item:
return True
else:
cur = cur.next
return False
def remove(self, item):
cur = self.__head
# 再定义一个游标pre
pre = None
while cur != None:
if cur.item == item:
# 当插入的位置是第一个时
if cur == self.__head:
self.__head = cur.next
else:
pre.next = cur.next
break
else:
# pre移到cur,cur向后走
pre = cur
cur = cur.next
完整代码及测试
class SingleLinkNode(object):
# 单链表节点
def __init__(self, item):
# 用来存储数据
self.item = item
# 存储下一个节点数据的地址
self.next = None
class SingleLinkList(object):
'''单链表'''
# 空链表
# 非空链表
def __init__(self, node = None):
# __head是头结点,如果该链表为空,他就是空的,如果非空,就是传入的
self.__head = node
def is_empty(self):
'''判断是否为空'''
# if self.__head == None:
# return True
# else:
# return False
return self.__head == None
def length(self):
'''链表的长度'''
# cur初始指向节点头
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.item, end=' ')
cur = cur.next
print(' ')
def append(self, item):
'''尾部添加'''
# 先创建一个新的节点
node = SingleLinkNode(item)
# 如果是一个空节点,让__head指向新节点
if self.is_empty():
self.__head = node
# 如果非空,利用游标遍历,当游标的下一个节点为空时,当前游标所指向的节点指向新节点
else:
cur = self.__head
while cur.next != None:
cur = cur.next
cur.next = node
def add(self, item):
'''头部添加'''
node = SingleLinkNode(item)
# 让新节点指向头部节点,即__head指向的节点
node.next = self.__head
# 再让__head指向新节点
self.__head = node
def insert(self, pos, item):
'''指定位置添加元素'''
# 如果小于等于0,头部添加
if pos <= 0:
self.add(item)
# 如果大于0,尾部添加
elif pos > self.length():
self.append(item)
# 找到指定位置
else:
node = SingleLinkNode(item)
cur = self.__head
count = 0
while count < (pos-1):
count += 1
cur = cur.next
# 先将新节点指向插入节点
node.next = cur.next
# 再将插入位置的前一个节点指向新节点
cur.next = node
def search(self, item):
'''查询某个值在链表中是否存在'''
cur = self.__head
while cur != None:
if cur.item == item:
return True
else:
cur = cur.next
return False
def remove(self, item):
'''删除节点'''
cur = self.__head
# 再定义一个游标pre
pre = None
while cur != None:
if cur.item == item:
# 当插入的位置是第一个时
if cur == self.__head:
self.__head = cur.next
else:
pre.next = cur.next
break
else:
# pre移到cur,cur向后走
pre = cur
cur = cur.next
s = SingleLinkList()
s.append(1)
s.append(12)
s.append(36)
s.add(520)
s.insert(-1, 7)
s.insert(5, 9)
s.insert(3, 110)
print(s.is_empty())
print(s.length())
s.travel()
print(s.search(520))
s.remove(7)
s.travel()
链表与顺序表的对比
链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的使用 要相对灵活。
操作 | 链表 | 顺序表 |
---|---|---|
访问元素 | O(n) | O(1) |
头部插入/删除 | O(1) | O(n) |
尾部插入/删除 | O(n) | O(1) |
中间插入/删除 | O(n) | O(n) |