Python进阶 - 数据结构与算法 - 03 单链表的操作

一、链表的引入

顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以使用起来并不是很灵活。

链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。

链表的结构
链表中的每一个节点都包括数据域和链接域两部分。数据域中存放真实的数据,链接域存放下一个数据的地址。

单链表结构
Python中变量标识的本质

a = 10 a本身是一个地址,该地址指向10
交叉赋值
当a,b = b,a其实相当于修改了a,b 两个变量的指向,将a指向了20,将b指向了20.

二、单链表的引入

2.1 节点的实现

链表中的每一个数据都由数据区和链接域两部分组成,在Python或者其他语言中没有一个数据类型可以同时储存这两个数据,因此我们可以定义节点这样一个类,而节点中的数据和链接区可以想象成该类的两个属性。

由于链表中的链接域存储的是下一个数据的地址,但在内存中并没有地址这一个明确的数据,因此我们可以将链接域的值设置为下一个节点。

在定义一个新的节点时,我们没有办法确定该节点的下一个节点,因此我们可以先将其设置为空,当有下一个节点时,在重新对其赋值。

        class SingleNode(object):
              """单链表的节点"""
		    def __init__(self,item,):
		    
		        #item存放的数据区域
		        self.item = item
		        
		        #next存放下一个节点的标识
		        self.next = None

2.2 单链表的操作

is_empty() 链表是否为空
length() 链表长度
travel() 遍历整个链表
append(item) 链表尾部添加元素
add(item) 链表头部添加元素
insert(pos, item) 指定位置添加元素
search(item) 查找节点是否存在
remove(item) 删除节点

2.3 单链表的操作实现

2.3.1 判断单链表是否为空

单链表的结构
如果单链表为空,则其首节点为空(self.__head = None);如果单链表不为空,则其首节点不为空(self.__head = node)。

  class SingleNode(object):
    	"""单链表的节点"""
	    def __init__(self,item):
	        self.item = item
	        self.next = None

  class SingleLinkList(object):

	    def __init__(self,node = None):
	        # 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
	        # 否则,等于实参值
	        self.__head = node
	
	    def is_empty(self):
	        """单链表是否为空"""
	        # if self.__head == None:
	        #    return True
	        # else:
	        #    return False
	        return self.__head == None

node = SingleNode(100)
s = SingleLinkList(node)
print(s.is_empty())

2.3.2 单链表的长度

遍历当前链表的长度
定义一个游标cur,让cur从头指针head出发直到遍历到为指针None
定义一个计数变量count,cur移动一次,count就加1

  class SingleNode(object):
    	"""单链表的节点"""
	    def __init__(self,item):
	        self.item = item
	        self.next = None

  class SingleLinkList(object):

	    def __init__(self,node = None):
	        # 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
	        # 否则,等于实参值
	        self.__head = node
	        
	    def length(self):
	        """链表长度"""
	        count = 0
	        cur = self.__head

	        while cur != None:
	            count += 1
	            cur = cur.next
	        return count

node = SingleNode(100)
s = SingleLinkList(node)
print(s.length())

2.3.3 遍历单链表

遍历链表和求链表长度的思想基本一致,唯一不同的就是我们不需要计数变量但每循环一次,我们就需要打印该节点的数据

  class SingleNode(object):
    	"""单链表的节点"""
	    def __init__(self,item):
	        self.item = item
	        self.next = None

  class SingleLinkList(object):

	    def __init__(self,node = None):
	        # 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
	        # 否则,等于实参值
	        self.__head = node
	        
	    def travel(self) :
	        """遍历整个链表"""
	        cur = self.__head
	
	        while cur != None:
	            #打印节点中的数据
	            print(cur.item,end = ' ')
	            cur = cur.next

node = SingleNode(100)
s = SingleLinkList(node)
s.travel()

2.3.4 向链表尾部添加元素

append()方法
向链表尾部添加节点时,需要进行两个操作:
1.创建一个新的节点 node = SingleNode(item)
2.遍历到最后一个节点,令其的next属性等于新的节点
cur.next == node
注意,当链表为空时,self.__head = None,而None类型没有next属性

  class SingleNode(object):
	    	"""单链表的节点"""
		    def __init__(self,item):
		        self.item = item
		        self.next = None
	
  class SingleLinkList(object):

	    def __init__(self,node = None):
	        # 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
	        # 否则,等于实参值
	        self.__head = node
	        
	    def append(self,item):
	        """链表尾部添加元素"""
	        node = SingleNode(item)
	
	        #空链表的情况
	        if self.is_empty():
	           self.__head = node
	        else:
	            cur = self.__head
	            #遍历找到最后一个节点
	            while cur.next != None:
	                    #添加新的节点
	                cur = cur.next
	            cur.next = node
     
  s = SingleLinkList()
  s.append(200)
  s.append(220)
  s.append(890)
  s.travel()

2.3.5 链表头部添加元素

add()方法
向链表头部添加节点,需要进行一下三步操作:
1.创建一个新的节点 node = SingleNode(item)
2.令原先的首指针等于新节点的下一个节点标识
node.next = self.__head
3.令新节点等于首指针 self.__head = node

  class SingleNode(object):
	    	"""单链表的节点"""
		    def __init__(self,item):
		        self.item = item
		        self.next = None
	
  class SingleLinkList(object):

	    def __init__(self,node = None):
	        # 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
	        # 否则,等于实参值
	        self.__head = node
	        
	    def add(self,item):
	        """链表头部添加元素"""
	        node = SingleNode(item)
	        node.next = self.__head
	        self.__head = node
     
  s = SingleLinkList()
  s.append(200)
  s.append(220)
  s.append(890)
  s.add(67)
  s.travel()

2.3.6 insert(pos, item) 指定位置添加元素

insert()方法
在指定位置插入节点需要进行以下三步操作
1.通过循环找到指定插入位置的前一个节点
count < pos-1
2.将当前节点下一个节点赋值给新节点的下一个节点标识
node.next = pre.next
3.将新节点赋值给当前节点的下一个节点标识
pre.next = node

需要考虑特殊两种情况,头部插入add()和尾部插入append()

  class SingleNode(object):
	    	"""单链表的节点"""
		    def __init__(self,item):
		        self.item = item
		        self.next = None
	
  class SingleLinkList(object):

	    def __init__(self,node = None):
	        # 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
	        # 否则,等于实参值
	        self.__head = node
	        
        def  insert(self,pos,item):
	        node = SingleNode(item)
	        count = 0
	        pre = self.__head
	
	        #考虑特殊情况:
	        #头部插入
	        if pos < 0:
	            self.add(item)
	
	        #尾部插入
	        elif pos > self.length():
	            self.append(item)
	
	        #指定位置插入
	        #通过循环找到指定位置的前一个位置
	        else:
	            while count < (pos -1):
	                count += 1
	                pre = pre.next
	            node.next = pre.next
	            pre.next = node
     
  s = SingleLinkList()
  s.append(200)
  s.append(220)
  s.append(890)
  s.insert(2,67)
  s.travel()

注意:节点位置从0开始。

2.3.7 查找节点是否存在

遍历链表,如果当前节点的item值等于所查找的item值,则返回True,如果不是则游标后移。如果遍历完整个链表仍没有找到,则返回False。

      class SingleNode(object):
	    	"""单链表的节点"""
		    def __init__(self,item):
		        self.item = item
		        self.next = None
	
  class SingleLinkList(object):

	    def __init__(self,node = None):
	        # 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
	        # 否则,等于实参值
	        self.__head = node
	        
        def search(self,item):

	        cur = self.__head
	        while cur != None:
	            if cur.item == item:
	                return True
	            else:
	                cur = cur.next
	        return False
	                 
  s = SingleLinkList()
  s.append(200)
  s.append(220)
  s.append(890)
  s.insert(2,67)
  s.travel()
  print(s.search(200))

2.3.8 删除节点

remove()方法
删除指定节点,具体操作如下
1.遍历链表,判断cur.item == item
2.如果相等,则令pre.next= cur.next
3.不相等则指针继续后移 pre= cur cur= cur.next
注意特殊情况,如果删除的是头节点则令self.__head = cur.next

  class SingleNode(object):
	    	"""单链表的节点"""
		    def __init__(self,item):
		        self.item = item
		        self.next = None
	
  class SingleLinkList(object):

	    def __init__(self,node = None):
	        # 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
	        # 否则,等于实参值
	        self.__head = node
	        
	    def remove(self,item):
	        pre = None
	        cur = self.__head
	
	        while cur != None:
	            #找到指定的节点
	            if cur.item == item:
	            
	                #当删除的是头节点时
	                if cur == self.__head:
	                    self.__head = cur.next
	                else:
	                    pre.next = cur.next
	                break 
	                  #如果不加break,则cur 一直是当前的值,不会移动,进入了死循环
	            else:
	               # 没有找到继续执行
	                pre = cur
	                cur = cur.next    
       
  s = SingleLinkList()
  s.append(200)
  s.append(220)
  s.append(890)
  s.insert(2,67)
  s.travel()
  print()
  s.remove(200)
  s.travel()

2.4 链表与顺序表的对比

操作顺序表复杂度链表复杂度
访问元素O(1)O(n)
在头部插入或删除O(n)O(1)
在尾部插入或删除O(1)O(n)
在中间插入或删除O(n)O(n)

写链表的代码建议

1.理解指针或引用的含义(pre、cur)
2.警惕指针丢失(先处理后一个节点,在处理前一个节点)
3.重点留意边界条件处理(空链表、头部插入或删除,尾部插入或删除)
4.举例画图,辅助思考

如何实现LRU缓存淘汰算法?

1.缓存是高数据读取技术,常见的包括CPU缓存、数据库缓存、浏览器缓存等。缓存大小有限,当缓存满时,就需要删除缓存中的部分数据。

2.缓存淘汰的三种常见策略:先进先出(FIFO First In,First Out)、最少使用策略(LFU Least FrequentlyUsed)、最近最少使用策略(LRU Least Recently Used)

3.我们可以这样来操作:
当有一个新的数据被访问时,我们从链表头开始顺序遍历链表。

a.如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据对应的结点,并将其从原来的位置删除,然后再插入到链表的头部。

b.如果此数据没有在缓存链表中,又可以分为两种情况:
如果此时缓存未满,则将此结点直接插入到链表的头部;
如果此时缓存已满,则链表尾结点删除,将新的数据结点插入链表的头部。

越靠近链表尾部的结点是越早之前访问的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值