首先我们先了解顺序表和链表的概念,为什么要使用链表。
顺序表和链表
顺序表
顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时需要进行数据搬迁,所以使用起来不是很灵活。
链表
链表(Linked List) 是一种常见的基础数据结构,是一只种类线性表,但是不像顺序表一样连续存储数据,而是在每一个节点(数据存储单元)里存放下一个节点的位置信息(即地址)。
链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。
顺序表和链表的内存分布
单向链表
单向链表也叫单链表,每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值。
• 表元素域elem 用来存放具体的数据。
• 链接域next 用来存放下一个节点的位置(python中的标识)
• 变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点。
单链表的操作
链表实现之前,需要先实现节点,代码如下:
class Node(object):
"""单链表节点的封装"""
def __init__(self, element):
self.element = element
self.next = None
我们先实现判断链表是否为空,链表长度和遍历链表的操作代码:
class Node(object):
"""单链表节点的封装"""
def __init__(self, element):
self.element = element
self.next = None
class SingleLink(object):
"""单链表的封装"""
def __init__(self):
# 默认为空
self._head = None
def is_empty(self):
"""是否为空"""
return self._head == None
def __len__(self):
"""
求链表长度
1. 判断是否为空,为空直接返回0
2. 不为空时依次遍历,长度加1之后将下一个节点赋值给当前
"""
if self.is_empty():
return 0
else:
cur = self._head
length = 0
while cur != None:
length += 1
cur = cur.next
return length
def travel(self):
"""遍历链表"""
if self.is_empty():
print('空链表')
else:
cur = self._head
while cur.next != None:
print(cur.element, end=',')
cur = cur.next
print(cur.element)
单链表的头部添加元素
class SingleLink(object):
"""单链表的封装"""
def __init__(self):
# 默认为空
self._head = None
def add(self, item):
"""
头部添加元素:
1. 创建一个保存item值的节点
2. 将新节点的next指向头结点,即_head指向的位置
3. 将链表的头_head指向新节点
:param item:
:return:
"""
node = Node(item)
node.next = self._head
self._head = node
指定位置添加元素
class SingleLink(object):
"""单链表的封装"""
def __init__(self):
# 默认为空
self._head = None
def insert(self, index, item):
"""
指定位置添加元素
1. 指定位置为头部之前,则头部添加元素
2. 指定位置为尾部之后,则尾部添加元素
3. 中间位置:需要找出指定位置的前一个元素,得到其尾部,插入的节点的尾部指向前面得到的尾部,
前一个元素新的尾部指向插入的头部
:param index:
:param item:
:return:
"""
if index <= 0:
self.add(item)
elif index >= len(self):
self.append(item)
else:
node = Node(item)
count = 0 # 当前节点的位置
cur = self._head
# 寻找插入节点的前一个节点
while count < index - 1:
count += 1
cur = cur.next
# 插入节点的前一个节点的尾部成为了插入节点的尾部指向
# 然后将插入节点的前一个节点的尾部指向更新为新插入节点的头部
node.next = cur.next
cur.next = node
删除节点
def remove(self, item):
"""
删除指定元素的节点
1. 删除头部节点
2. 删除其他位置的节点
:param item:
:return:
"""
"""
既然要删除当前节点,首先想的就是遍历链表找到对应节点并且删除,
这里直接省去了遍历的操作,直接给出了对应的节点。
既然想删除当前节点那么将给定的节点的val和next全部转换成当前节点的下一个节点所对应的值,
那么当前节点在本链表中就相当于替换成了下一个节点。
"""
cur = self._head
pre = None
while cur != None:
if cur.element == item:
if not pre:
self._head = cur.next
else:
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
尾部添加元素
def append(self, item):
"""
尾部添加元素
1. 先判断链表是否为空,若为空,将_head指向新节点
2. 若不为空,找到尾部,将尾节点next指向新节点
:param item:
:return:
"""
node = Node(item)
if self.is_empty():
self._head = node
else:
cur = self._head
while cur.next != None:
cur = cur.next
if cur.next == None:
cur.next = node
判断元素在链表中是否存在
def search(self, item):
"""
判断查找的元素在节点中是否存在,返回Bool类型
:param item:
:return:
"""
cur = self._head
while cur.next != None:
if cur.element == item:
return True
cur = cur.next
if cur.element == item:
return True
return False
链表与顺序表的对比
操作 | 链表 | 顺序表 |
---|---|---|
访问元素 | O(n) | O(1) |
在头部插入/删除 | O(1) | O(n) |
在尾部插入/删除 | O(n) | O(1) |
在中间插入/删除 | O(n) | O(n) |
时间复杂度的不同主要是因为链表和顺序表在插入和删除时进行的是完全不同的操作。链表的主要耗时操作是遍历查找。顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前后移位操作,只能通过拷贝和覆盖的方法进行。
python实现单向链表的整体代码
class Node(object):
"""单链表节点的封装"""
def __init__(self, element):
self.element = element
self.next = None
class SingleLink(object):
"""单链表的封装"""
def __init__(self):
# 默认为空
self._head = None
def is_empty(self):
"""是否为空"""
return self._head == None
def __len__(self):
"""
求链表长度
1. 判断是否为空,为空直接返回0
2. 不为空时依次遍历,长度加1之后将下一个节点赋值给当前
"""
if self.is_empty():
return 0
else:
cur = self._head
length = 0
while cur != None:
length += 1
cur = cur.next
return length
def travel(self):
"""遍历链表"""
if self.is_empty():
print('空链表')
else:
cur = self._head
while cur.next != None:
print(cur.element, end=',')
cur = cur.next
print(cur.element)
def append(self, item):
"""
尾部添加元素
1. 先判断链表是否为空,若为空,将_head指向新节点
2. 若不为空,找到尾部,将尾节点next指向新节点
:param item:
:return:
"""
node = Node(item)
if self.is_empty():
self._head = node
else:
cur = self._head
while cur.next != None:
cur = cur.next
if cur.next == None:
cur.next = node
def add(self, item):
"""
头部添加元素:
1. 创建一个保存item值的节点
2. 将新节点的next指向头结点,即_head指向的位置
3. 将链表的头_head指向新节点
:param item:
:return:
"""
node = Node(item)
node.next = self._head
self._head = node
def insert(self, index, item):
"""
指定位置添加元素
1. 指定位置为头部之前,则头部添加元素
2. 指定位置为尾部之后,则尾部添加元素
3. 中间位置:需要找出指定位置的前一个元素,得到其尾部,插入的节点的尾部指向前面得到的尾部,
前一个元素新的尾部指向插入的头部
:param index:
:param item:
:return:
"""
if index <= 0:
self.add(item)
elif index >= len(self):
self.append(item)
else:
node = Node(item)
count = 0 # 当前节点的位置
cur = self._head
# 寻找插入节点的前一个节点
while count < index - 1:
count += 1
cur = cur.next
# 插入节点的前一个节点的尾部成为了插入节点的尾部指向
# 然后将插入节点的前一个节点的尾部指向更新为新插入节点的头部
node.next = cur.next
cur.next = node
def remove(self, item):
"""
删除指定元素的节点
1. 删除头部节点
2. 删除其他位置的节点
:param item:
:return:
"""
"""
既然要删除当前节点,首先想的就是遍历链表找到对应节点并且删除,
这里直接省去了遍历的操作,直接给出了对应的节点。
既然想删除当前节点那么将给定的节点的val和next全部转换成当前节点的下一个节点所对应的值,
那么当前节点在本链表中就相当于替换成了下一个节点。
"""
cur = self._head
pre = None
while cur != None:
if cur.element == item:
if not pre:
self._head = cur.next
else:
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
def search(self, item):
"""
判断查找的元素在节点中是否存在,返回Bool类型
:param item:
:return:
"""
cur = self._head
while cur.next != None:
if cur.element == item:
return True
cur = cur.next
if cur.element == item:
return True
return False
if __name__ == '__main__':
# 实例化单链表对象
link = SingleLink()
# 长度获取
# print("链表长度:", len(link))
# # 遍历链表
# link.travel()
# print("链表是否为空?", link.is_empty())
link.add(3)
print(link.travel())
link.append(1)
link.append(2)
print(link.travel())
print("链表是否为空?", link.is_empty())
link.insert(1, 'hello')
print(link.travel())
print("链表长度:", len(link))
link.remove(3)
print(link.travel())
print(link.search(2))