目录
简介
单链表
循环链表
双向链表
链表应用
链表是属于线性表的一种数据结构,其优势在于可以实现快速的插入、删除,但对于查询,其相比于数组时间复杂度大,链表主要是通过指针将一组零散的内存块连接起来,我们可以把每个内存块当成一个节点,与数组不同,链表不需要连续的内存空间,链表的结构多种多样,我们常用的有单链表、循环链表、双链表,接下来我们会一一进行介绍,对于链表,我们一般实现以下基本的功能:
头部添加元素 | add_head() |
尾部添加元素 | add_tail() |
中间添加元素 | insert() |
查看链表是否为空 | is_empty() |
链表长度 | length() |
判断某个元素是否存在 | search() |
遍历链表 | show_linklist() |
首先我们给出单链表的基本结构,如下所示:
其中data表示保存在当前节点的数据、next表示指向下一个即记录下个节点地址的指针,称之为后继指针next
其中第一个节点我们称之为头节点、最后一个节点成之为尾节点,单链表的删除和添加比较简单,我们给出图例,后续针对需要的实现的功能会给出具体代码,插入节点时,如下所示:
删除节点时,如下所示:
上面我们通过图形展示,大家有了直观的认识,接下来我们通过代码实现单链表的相关操作,代码如下:
'''
* @Author: Jack Shan
* @Date: 2020-07-22 17:26:07
* @Last Modified by: Jack Shan
* @Last Modified time: 2020-07-22 17:26:07
'''
class NewNode(object):
def __init__(self, data=None):
self.data = data # 存储当前节点数据
self.next = None # 指向下一个节点的指针
class SignalLinkList(object):
def __init__(self):
self.__head = None
# 判断节点是否为空
def is_empty(self):
return not self.__head
# 计算节点长度
def length(self):
if not self.__head:
return 0
sum = 1
node = self.__head
while node:
sum = sum + 1
node = node.next
return sum
# 头部添加数据
def add_head(self, data):
new_node = NewNode(data)
if not self.__head:
self.__head = new_node
else:
new_node.next = self.__head
self.__head = new_node
# 尾部添加数据
def add_tail(self, data):
new_node = NewNode(data)
if not self.__head:
self.__head = new_node
else:
node = self.__head
while node.next:
node = node.next
node.next = new_node
# 中间位置添加数据
def insert_data(self, index, data):
new_node = NewNode(data)
if index <= 0:
self.add_head(data)
return
if index > self.length()-1:
self.add_tail(data)
return
node = self.__head
for _ in range(index-1):
node = node.next
new_node.next = node.next
node.next = new_node
# 删除节点数据
def delete_node(self, data):
node = self.__head
pre = None
while node:
if node.data == data:
if node == self.__head:
self.__head = self.__head.next
return
pre.next = node.next
return
pre = node
node = node.next
# 查找节点是否存在
def search(self, data):
node = self.__head
while node.next:
if node.data == data:
return True
node = node.next
return False
# 遍历节点
def show_node(self):
node = self.__head
while node.next:
yield node.data
node = node.next
yield node.data
if __name__ == "__main__":
node = NewNode(10)
create_node = SignalLinkList()
print(create_node.is_empty())
create_node.add_head(22)
create_node.add_tail(33)
print(list(create_node.show_node()))
print(create_node.is_empty())
create_node.insert_data(1, 44)
create_node.add_head(92)
create_node.add_tail(3)
print(list(create_node.show_node()))
print(create_node.search(33))
print(create_node.length())
create_node.delete_node(44)
print(list(create_node.show_node()))
print(list(create_node.reverse1()))
print(list(create_node.reverse2()))
上面我们给出了单链表的基本功能,其还有一些其他的功能,如:单链表的反转、查找链表的中间节点、链表中环的判断等等,后续会结合leetcode给大家介绍,本文主要讲解链表的基本功能。
同样我们给出循环链表的基本结构,如下:
其与单链表的不同是,最后结束的尾节点的指针指向最开始的节点,即形成闭环的形式,当我们要处理的数据具有环型结构时,就比较适合采用循环链表的数据结构,同样我们也给出基本功能的实现代码,如下:
'''
* @Author: Jack Shan
* @Date: 2020-07-22 17:26:07
* @Last Modified by: Jack Shan
* @Last Modified time: 2020-07-22 17:26:07
'''
class NewNode(object):
def __init__(self, data=None):
self.data = data # 存储当前节点数据
self.next = None # 指向下一个节点的指针
class CircleLinkList(object):
def __init__(self):
self.__head = None
# 判断节点是否为空
def is_empty(self):
return not self.__head
# 计算节点长度
def length(self):
# 判断是否为空,若为空直接返回0
if not self.__head:
return 0
sum = 1
node = self.__head
# 循环到初始位置结束
while node.next != self.__head:
sum = sum + 1
node = node.next
return sum
# 头部添加数据
def add_head(self, data):
new_node = NewNode(data)
# 判断是否为空,若为空则在头部后添加,并形成闭环
if not self.__head:
self.__head = new_node
new_node.next = self.__head
else:
new_node.next = self.__head
node = self.__head
# 需要遍历至末尾,然后将末尾的指针指向新添加的节点
while node.next != self.__head:
node = node.next
node.next = new_node
# head指向新的开始节点
self.__head = new_node
# 尾部添加数据
def add_tail(self, data):
new_node = NewNode(data)
# 如果为空,则直接添加,同时新节点的指针指向头
if not self.__head:
self.__head = new_node
new_node.next = self.__head
else:
node = self.__head
# 遍历至末尾,末尾的指针指向新节点,新节点的指针指向头
while node.next != self.__head:
node = node.next
node.next = new_node
new_node.next = self.__head
# 中间位置添加数据
def insert_data(self, index, data):
new_node = NewNode(data)
# 判断添加位置是否为头部
if index <= 0:
self.add_head(data)
# 判断添加位置是否为尾部
elif index > self.length()-1:
self.add_tail(data)
# 其与单链表类似
else:
node = self.__head
for _ in range(index-1):
node = node.next
new_node.next = node.next
node.next = new_node
# 删除节点数据
def delete_node(self, data):
if not self.__head:
return
node = self.__head
pre = None
# 如果第一个元素是需要删除的元素
if node.data == data:
# 如果元素不止一个
if node.next != self.__head:
while node.next != self.__head:
node = node.next
# 末尾节点指向头部节点的下一个节点(即跳过删除的节点)
node.next = self.__head.next
# 调整头部节点
self.__head = self.__head.next
else:
self.__head = None
else:
# 如果不是第一个元素
pre = self.__head
while node.next != self.__head:
if node.data == data:
# 删除该节点
pre.next = node.next
return True
else:
# 记录当前节点的前一个指针
pre = node
# 继续下一个节点
node = node.next
# 删除元素在末尾
if node.data == data:
pre.next = self.__head
return True
# 查找节点是否存在
def search(self, data):
node = self.__head
while node.next != self.__head:
if node.data == data:
return True
node = node.next
return False
# 遍历节点
def show_node(self):
node = self.__head
while node.next != self.__head:
# if node.data:
# print(node.data, end=' --> ')
yield node.data
node = node.next
yield node.data
if __name__ == "__main__":
node = NewNode(10)
create_node = CircleLinkList()
print(create_node.is_empty())
create_node.add_head(22)
create_node.add_tail(33)
print(list(create_node.show_node()))
print(create_node.is_empty())
create_node.insert_data(1, 44)
create_node.add_head(92)
create_node.add_tail(3)
print(list(create_node.show_node()))
print(create_node.search(33))
print(create_node.length())
create_node.delete_node(44)
print(list(create_node.show_node()))
前面讲到的单链表和循环链表均只有一个方向,而双向链表如其名所述,可以实现两个方向的数据遍历遍历,具有前后两个指针即后继指针、前驱指针,双向链表需要两个空间来存储后继节点和前驱及节点,因此对于同样多的数据,双向链表比单向链表需要更多的内存空间,但是其可以双向遍历,具有更高的灵活性,同样,我们给出图示结构:
其实现基本的功能代码如下:
'''
* @Author: Jack Shan
* @Date: 2020-07-22 22:15:15
* @Last Modified by: Jack Shan
* @Last Modified time: 2020-07-22 22:15:15
'''
class NewNode(object):
def __init__(self, data=None):
self.data = data # 存储当前节点数据
self.next = None # 指向下一个节点的指针
self.pre = None # 指向前一个节点的指针
class DoubleLinkList(object):
def __init__(self):
self.__head = None
# 判断是否为空
def is_empty(self):
return not self.__head
# 计算链表长度
def length(self):
node = self.__head
sum = 0
while node:
sum = sum + 1
node = node.next
return sum
# 头部插入元素
def add_head(self, data):
new_node = NewNode(data)
# 如果链表为空
if not self.__head:
self.__head = new_node
else:
# 链表的新节点的后指针指向原来头部节点
new_node.next = self.__head
# 原来头部的前指针指向新节点
self.__head.pre = new_node
# 头部节点指向新节点
self.__head = new_node
# 尾部添加元素
def add_tail(self, data):
new_node = NewNode(data)
# 如果链表为空
if not self.__head:
self.__head = new_node
else:
node = self.__head
# 遍历到结尾
while node.next:
node = node.next
# 新节点的前指针指向原来的尾节点
new_node.pre = node
# 原来的尾节点的后指针指向新节点
node.next = new_node
# 中间插入元素
def insert_data(self, index, data):
new_node = NewNode(data)
# 判断添加位置是否为头部
if index <= 0:
self.add_head(data)
# 判断添加位置是否为尾部
elif index > self.length()-1:
self.add_tail(data)
else:
node = self.__head
for _ in range(index-1):
node = node.next
new_node.next = node.next
new_node.pre = node
node.next.pre = new_node
node.next = new_node
# 遍历节点
def show_node(self):
node = self.__head
while node.next:
yield node.data
node = node.next
yield node.data
# 查询是否存在
def search(self, data):
return data in self.show_node()
# 删除某个元素
def delete_node(self, data):
if self.is_empty():
return
node = self.__head
# 如果删除的是第一个节点上元素
if node.data == data:
# 只有一个元素
if node.next:
self.__head = None
return True
else:
self.__head = node.next
node.next.pre = None
return True
# 删除中间的某个元素
while node.next:
if node.data == data:
# 当前节点的前指针指向的节点的后指针指向删除后节点的下一个节点
node.pre.next = node.next
node.next.pre = node.pre
return True
node = node.next
# 如果是最后的元素
if node.data == data:
node.pre.next = None
return True
if __name__ == "__main__":
create_node = DoubleLinkList()
print(create_node.is_empty())
create_node.add_head(22)
create_node.add_tail(33)
print(list(create_node.show_node()))
print(create_node.is_empty())
create_node.insert_data(1, 44)
create_node.add_head(92)
create_node.add_tail(3)
print(list(create_node.show_node()))
print(create_node.search(33))
print(create_node.length())
create_node.delete_node(44)
print(list(create_node.show_node()))
LRU缓存
缓存系统,缓存是一种提高数据读取性能的技术,在硬件设计和软件开发中有着广泛的应用,如CPU缓存、数据库缓存、浏览器缓存等等,当缓存被占用满时,需要清除缓存,一般有三种策略:先进先出策略FIFO(First In First Out)、最少使用策略LFU(Least Frequently Used)、最近最少使用策略LRU(Least Recently Used),对于LRU缓存,,可以通过链表来实现,具体实现原理如下:
1、当数据已经在缓存链表中,则将其在链表中原来的位置删除,将其插入链表头部
2、当数据没有在缓存中,并且缓存未满,则直接将数据插入链表头部
3、当数据没有在缓存中,并且缓存已满,则将链表末尾的数据删除,将新数据插入链表头部