03-链表
链表的存储结构如图所示:
单链表:
循环链表:
双向链表:
双向循环链表
单向链表添加数据动图:
单链表代码实现:
#单链表的实现 class ListNode: def __init__(self,data=None): self.data = data self.next = None def __repr__(self): """ 输出格式化信息 :return: """ return "node: {}".format(self.data) class SingelLinkedList: def __init__(self, node=None): '''创建头节点_head指向第一个节点node''' self.__head = node def is_empty(self): '''判断是否空链表''' return self.__head is None def length(self): '''链表的长度''' current = self.__head # 表示当前节点 count = 0 # 用来统计已遍历的节点数目 while current is not None: count += 1 current = current.next # 向后移动游标 return count def ergodic(self): '''遍历整个链表''' current = self.__head # 游标,用来表示当前节点 while current is not None: yield current.data # 生成当前节点的值 current = current.next # 向后移动游标 def append(self, data): '''链表尾部添加节点''' node = ListNode(data) if self.is_empty(): # 如果是空链表 self.__head = node else: current = self.__head while current.next is not None: current = current.next # 此时,current 表示尾节点,将它的 next 指向新增的节点即可 current.next = node def head_add(self, data): '''在链表的头部添加节点''' node = ListNode(data) node.next = self.__head # 将新增节点的 next 指向原来的头节点 self.__head = node # 再将链表的 __head 属性指向新增节点即可 def insert(self, pos, data): '''在指定位置添加节点。 pos 表示节点的下标,为了跟列表等一样,这里规定 pos 从0开始计数,即头节点的 pos 为0''' if pos <= 0: # 暂时不支持负数的下标。如果传入负数或者0,统一在头部插入 self.head_add(data) elif pos > self.length() - 1: # 如果传入的下标大于链表的长度减去1,默认使用在尾部插入 self.append(data) else: pre = self.__head # 用来表示 pos - 1 位置的节点 count = 0 while count < pos - 1: # 当count = pos - 1时,表示已经找到了 pos - 1 的节点,退出循环 count += 1 pre = pre.next # 向后移动游标 # 退出循环后,pre 指向 pos - 1 所在的节点 node = ListNode(data) node.next = pre.next # 将新增节点的 next 指向 pre 的下一节点 pre.next = node # 再将 pre 的 next 重新指向新增的节点即可 def search(self, data): '''查找元素''' current = self.__head while current is not None: if current.data == data: return True else: current = current.next return False def remove(self, data): '''链表中删除元素,跟列表中的 remove() 类似,也是删除第一个遇到的元素''' pre = None current = self.__head while current is not None: # 遍历整个链表 if current.data == data: # 表示找到了要删除的节点 #特殊1.要删除的节点是头节点时 #特殊2.链表只有一个节点,刚好它就是要删除的节点,其实跟上面一样,也是头节点 if current == self.__head: self.__head = current.next else: # 此时,current 指向要删除的节点,pre 指向要删除的节点的前一节点 pre.next = current.next # 特殊情况3: 如果要删除的节点是尾节点时,也适合 # 当找到要删除的节点时,需要退出 while 循环 break else: pre = current # 先让 pre 指向当前节点 current = current.next # 再让 current 向后移动
缓存是一种提高数据读取性能的技术,在硬件设计、软件开发中都有着非常广泛的应用,比如常见的 CPU(内存) 缓存、数据库缓存、浏览器缓存等等。
1、先进先出策略 FIFO(First In,First Out)
2、最少使用策略 LFU(Least Frequently Used)
3、最近最少使用策略 LRU(Least Recently Used)
LRU缓存代码实现:
class ListNode(object): def __init__(self, val, n=None): self.val = val self.next = n class LRUCache: """ 一个 LRU 缓存 维护了一个有序单链表,越靠近链表尾部的结点是越早之前访问的。 """ def __init__(self, capacity: int = 10): self.cap = capacity # 哨兵节点, 本身不存储任何数据 self.head = ListNode(None, None) self.length = 0 def __len__(self): return self.length def get(self, val: object) -> bool: '''获取指定缓存数据 思路:从链表头开始顺序遍历链表: 1. 如果此数据之前已经被缓存在链表中了,遍历得到这个数据对应的结点,并将其从原来的位置删除,然后再插入到链表的头部。 2. 如果此数据没有在缓存链表中,则将新的数据结点插入链表的头部: - 如果此时缓存已满已超过容量,则将链表尾结点删除, 参数: val:要获取的数据 返回: 存在于缓存中,返回True,否则返回 False。 ''' prev = None # 用于记录尾节点的前一个节点 p = self.head # 如果此数据之前已经被缓存在链表中了 while p.next: if p.next.val == val: # 将目标节点从原来的位置删除 dest = p.next # dest临时保存目标节点 p.next = dest.next # 将目标节点插入到头部 self.insert_to_head(self.head, dest) return True prev = p p = p.next # 如果此数据没有缓存在链表中 self.insert_to_head(self.head, ListNode(val)) self.length += 1 # 添加数据导致超过容量则要删除尾节点 if self.length > self.cap: prev.next = None return False @staticmethod def insert_to_head(head, node): """将指定节点插入到头部""" node.next = head.next head.next = node def __str__(self): vals = [] p = self.head.next while p: vals.append(str(p.val)) p = p.next return '->'.join(vals)