文章目录
一、节点的实现&链表的初始化
节点组成部分:item next
1.定义节点类
class SingleNode(object):
'''the node of single_link_list'''
def __init__(self,item):
'''初始化'''
#item存放数据元素
self.item = item
#next是下一个节点的标识
self.next = None
2.定义单链表并初始化
链表为空:
__head == None
链表不为空:
__head = nodex
。
class SingleLinkList(object):
'''单链表'''
def __init__(self,node = None):
self.__head = node
3.单链表的操作
is_empty()
链表是否为空
length()
链表长度
travel()
遍历整个链表
append(item)
链表尾部添加元素
add(item)
链表头部添加元素
insert(pos, item)
指定位置添加元素
search(item)
查找节点是否存在
remove(item)
删除节点
二、单链表中的遍历&长度&末尾添加
1.获取长度:
用游标遍历cur = cur.next
,
最后指向Nonecur == None
,即停止.
用count计数,即每遍历一个节点,count += 1
.
def length(self):
'''链表长度'''
#cur即游标,指向首节点,用来遍历
cur = self.__head
count = 0
#当未到达尾部时
while cur != None:
count += 1
#将游标向后移动一位
cur = cur.next
return count
2.遍历链表:
指标每次指向下一个节点
应该先打印出首节点,再移向下一个节点。
def travel(self):
'''遍历链表'''
cur = self.__head
while cur != None:
print(cur.item,end=' ')
cur = cur.next
3.append尾部追加元素
先创建节点,再让游标向后遍历,直到cur.next = node
要先判断链表是否为空。
def append(self,item):
'''链表尾部添加元素'''
#创建一个保存数据的节点
node = SingleNode(item)
#判断链表是否为空,若为空,则将__head指向新节点
if self.is_empty():
self.__head = node
#若不为空,则找到尾部
else:
cur = self.__head
while cur.next != None:
#找到最后一个节点
cur = cur.next
#将尾节点的next指向新节点
cur.next = node
三、单链表中的头部添加&指定位置添加
1.add头部添加元素
先将要添加的节点指向头节点,再将head指向要添加的节点。
def add(self,item):
'''链表头部添加元素'''
# 创建一个保存数据的节点
node = SingleNode(item)
#将新节点的链接区指向头节点
node.next = self.__head
#将链表的头节点指向新节点
self.__head = node
2.insert指定位置添加元素
新节点指向当前节点的下一个节点node.next = cur.next
,当前节点指向新节点cur.next = node
def insert(self,pos,item):
'''指定位置添加元素'''
#若指定位置为第一个位置之前,则指向头部插入
if pos <= 0:
self.add(item)
#若指定位置超过链表尾部,则执行尾部插入
elif pos > self.length() - 1:
self.append(item)
#找到指定位置
else:
node = SingleNode(item)
# pre用来指向指定位置pos的前一个位置pos-1,初始从头节点开始移动到指定位置
pre = self.__head
count = 0
while count < pos - 1:
count += 1
pre = pre.next
#将新节点node的next指向插入位置的节点
node.next = pre.next
#将插入位置的前一个节点的next指向新节点
pre.next = node
四、单链表的搜索和删除
1.查找
要遍历到最后一个节点后的None,如果没有找到,返回False。
def search(self,item):
'''查找节点是否存在'''
cur = self.__head
while cur != None:
#判断第一个节点
if cur.item == item:
return True
#未找到,继续向后移动
else:
cur = cur.next
return False
2.remove删除指定元素
将被删除节点的上一个节点指向被删除节点的下一个节点,进行判断。
def remove(self,item):
'''删除节点'''
cur = self.__head
pre = None
while cur != None:
#找到指定元素
if cur.item == item:
#如果第一个就是删除的节点,将头指针指向头结点的后一个节点
if cur == self.__head:
self.__head = cur.next
#否则,将删除位置前一个节点的next指向删除位置的后一个节点
else:
pre.next = cur.next
break
#未找到,继续移动
else:
pre = cur
cur = cur.next
3.完整实现
整个节点和单链表的完整实现和测试代码为:
#单链表
#节点的实现
class SingleNode(object):
'''the node of single_link_list'''
def __init__(self,item):
'''item存放数据元素'''
self.item = item
'''next是下一个节点的标识'''
self.next = None
class SingleLinkList(object):
'''单链表'''
def __init__(self,node = None):
self.__head = node
def is_empty(self):
'''判断是否为空'''
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
def append(self,item):
'''链表尾部添加元素'''
#创建一个保存数据的节点
node = SingleNode(item)
#判断链表是否为空,若为空,则将__head指向新节点
if self.is_empty():
self.__head = node
#若不为空,则找到尾部
else:
cur = self.__head
while cur.next != None:
#找到最后一个节点
cur = cur.next
#将尾节点的next指向新节点
cur.next = node
def add(self,item):
'''链表头部添加元素'''
# 创建一个保存数据的节点
node = SingleNode(item)
#将新节点的链接区指向头节点
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 = SingleNode(item)
# pre用来指向指定位置pos的前一个位置pos-1,初始从头节点开始移动到指定位置
pre = self.__head
count = 0
while count < pos - 1:
count += 1
pre = pre.next
#将新节点node的next指向插入位置的节点
node.next = pre.next
#将插入位置的前一个节点的next指向新节点
pre.next = node
def remove(self,item):
'''删除节点'''
cur = self.__head
pre = None
while cur != None:
#找到指定元素
if cur.item == item:
#如果第一个就是删除的节点,将头指针指向头结点的后一个节点
if cur == self.__head:
self.__head = cur.next
#否则,将删除位置前一个节点的next指向删除位置的后一个节点
else:
pre.next = cur.next
break
#未找到,继续移动
else:
pre = cur
cur = cur.next
def search(self,item):
'''查找节点是否存在'''
cur = self.__head
while cur != None:
#判断第一个节点
if cur.item == item:
return True
#未找到,继续向后移动
else:
cur = cur.next
return False
#测试
#定义一个值为100的节点
node = SingleNode(100)
s = SingleLinkList()
s.append(1)
s.append(2)
s.append(3)
s.append(4)
s.append(5)
s.add(6)
s.insert(-1,7)
s.travel()
s.insert(3,8)
s.insert(10,9)
s.remove(7)
s.remove(3)
s.remove(9)
s.travel()
print(s.is_empty())
print(s.length())
s.travel()
运行结果为:
7 6 1 2 3 4 5 6 1 8 2 4 5 False
6
6 1 8 2 4 5
五、链表与顺序表的对比
链表没有顺序表随机读取的优点,同时增加了节点的指针域,增大了空间开销,但是对内存的使用相对更灵活。
操作 | 链表 | 顺序表 |
---|---|---|
访问元素 | O(n) | O(1) |
头部插入/删除 | O(1) | O(n) |
尾部插入/删除 | O(n) | O(1) |
在中间插入/插入 | O(n) | O(1) |
链表的主要耗时操作时遍历查找,删除和插入操作本身时间复杂度很小,为O(1);
顺序表查找很快,主要耗时的操作是拷贝覆盖。
写单链表的思考、启发和建议:
-
考虑各种特殊情况,如为空链表时插入、插入的位置为负;
-
对于遍历最后的判断条件,根据不同的操作要求分别判断,如
cur != None
和cur.next != None
等,还要注意其他边界条件; -
引入有助于理解和实现的辅助工具,如
cur
和pre
等,理解其意义有助于更好地实现; -
可以举例画图,有助于深入思考和理解。
利用链表实现LRU缓存算法
缓存是一种提高数据读取性能的技术,在硬件设计、软件开发中都有着非常广泛的应用,如CPU缓存、数据库缓存、浏览器缓存等等,Redis、MongoCache等等。
缓存的大小有限,当缓存被用满时,哪些数据应该被清理出去,哪些数据应该被保留?这就需要缓存淘汰策略来决定。常见的策略有三种:先进先出策略FIFO(First In,First Out)、最少使用策略LFU(Least Frequently Used)、最近最少使用策略LRU(Least Recently Used)。
实现的简单思路:
维护一个有序单链表,越靠近链表尾部的结点是越早之前访问的。当有一个新的数据被访问时,我们从链表头开始顺序遍历链表:
1.如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据对应的结点,并将其从原来的位置删除,然后再插入到链表的头部。
2.如果此数据没有在缓存链表中,又可以分为两种情况:
如果此时缓存未满,则将此结点直接插入到链表的头部;
如果此时缓存已满,则链表尾结点删除,将新的数据结点插入链表的头部。
大家也可以关注我的公众号:Python极客社区,在我的公众号里,经常会分享很多Python的文章,而且也分享了很多工具、学习资源等。另外回复“电子书”还可以获取十本我精心收集的Python电子书