《算法通关之路》-chapter7设计

《算法通关之路》学习笔记,记录一下自己的刷题过程,详细的内容请大家购买作者的书籍查阅。

最小栈

力扣第155题
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。

'''
方法一:辅助栈
时间复杂度:O(1)
空间复杂度:O(n)
'''
class MinStack:

    def __init__(self):
        self.stack = []
        self.min_stack = []

    def push(self, val: int) -> None:
        self.stack.append(val)
        if not self.min_stack:
            self.min_stack.append(val)
        elif val < self.min_stack[-1]:
            self.min_stack.append(val)
        else:
            self.min_stack.append(self.min_stack[-1])

    def pop(self) -> None:
        self.stack.pop()
        self.min_stack.pop()

    def top(self) -> int:
        return self.stack[-1]        

    def getMin(self) -> int:
        return self.min_stack[-1]
'''
方法二:改进空间的辅助栈
时间复杂度:O(1)
空间复杂度:O(n)
'''
class MinStack:

    def __init__(self):
        self.stack = []
        self.min_stack = []

    def push(self, x: int) -> None:
        self.stack.append(x)
        if not self.min_stack or x <= self.min_stack[-1]:
            self.min_stack.append(x)

    def pop(self) -> None:
        top = self.stack.pop()
        if self.min_stack and top == self.min_stack[-1]:
            self.min_stack.pop()
        return top

    def top(self) -> int:
        return self.stack[-1]        

    def getMin(self) -> int:
        return self.min_stack[-1]
'''
方法三:差值法(入栈“入栈值与最小值的差值”)
时间复杂度:O(1)
空间复杂度:O(1)
'''
class MinStack:

    def __init__(self):
        self.stack = []
        self.min = float('inf')

    def push(self, x: int) -> None:
        if not self.stack:
            self.min = x
            self.stack.append(0)
        else:
            self.stack.append(x - self.min)
            if x < self.min:
                self.min = x

    def pop(self) -> None:
        top = self.stack.pop()
        if top < 0:
            self.min = self.min - top
            return self.min
        return top + self.min

    def top(self) -> int:
        top = self.stack[-1]
        if top < 0:
            return self.min
        return top + self.min

    def getMin(self) -> int:
        return self.min

实现 Trie (前缀树)

力扣第208题
Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie() 初始化前缀树对象。
void insert(String word) 向前缀树中插入字符串 word 。
boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。

class TrieNode:
    def __init__(self):
        self.isEnd = False
        self.children = {}

class Trie:

    def __init__(self):
        self.root = TrieNode()

    def insert(self, word: str) -> None: # 插入一个字符串
        cur = self.root
        for ch in word:
            if ch not in cur.children:
                node = TrieNode()
                cur.children[ch] = node
            cur = cur.children[ch]
        cur.isEnd = True

    def search(self, word: str) -> bool: # 搜索一个字符串
        cur = self.root
        for ch in word:
            if ch not in cur.children:
                return False
            cur = cur.children[ch]
        
        if cur.isEnd:
            return True
        return False

    def startsWith(self, prefix: str) -> bool: # 是否包含值为value的前缀
        cur = self.root
        for ch in prefix:
            if ch not in cur.children:
                return False
            cur = cur.children[ch]
        
        return True

LRU 缓存

力扣第146题
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

'''
方法一:list + dict存储下标(超时)
'''
class LRUCache: # 最近最少使用

    def __init__(self, capacity: int):
        self.cache = list()
        self.cache_size = capacity
        self.idx_dict = dict()

    def move_to_tail(self, key: int):
        idx = self.idx_dict[key]
        k, v = self.cache.pop(idx)
        for i in range(idx, len(self.cache)):
            self.idx_dict[self.cache[i][0]] -= 1
        self.cache.append([k, v])
        self.idx_dict[k] = len(self.cache) - 1

    def get(self, key: int) -> int:
        if key in self.idx_dict.keys():
            self.move_to_tail(key)
            return self.cache[self.idx_dict[key]][1]
        return -1

    def put(self, key: int, value: int) -> None:
        if key in self.idx_dict.keys():
            self.cache[self.idx_dict[key]][1] = value
            self.move_to_tail(key)
        else:
            if len(self.cache) == self.cache_size:
                self.idx_dict.pop(self.cache[0][0])
                self.cache.pop(0)
                for k in self.idx_dict.keys():
                    self.idx_dict[k] -= 1
            self.cache.append([key, value])
            self.idx_dict[key] = len(self.cache) - 1
'''
方法二:双向链表 + dict存储下标
'''
class ListNode:
    def __init__(self, key=None, value=None):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache: # 最近最少使用

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.node_dict = dict()
        self.head = ListNode()
        self.tail = ListNode()
        self.head.next = self.tail
        self.tail.prev = self.head

    def move_to_tail(self, key: int):
        node = self.node_dict[key]
        node.prev.next = node.next
        node.next.prev = node.prev
        node.prev = self.tail.prev
        node.next = self.tail
        self.tail.prev.next = node
        self.tail.prev = node

    def get(self, key: int) -> int:
        if key in self.node_dict:
            self.move_to_tail(key)
            return self.node_dict[key].value
        return -1

    def put(self, key: int, value: int) -> None:
        if key in self.node_dict:
            self.node_dict[key].value = value
            self.move_to_tail(key)
        else:
            if len(self.node_dict) == self.capacity:
                self.node_dict.pop(self.head.next.key)
                self.head.next = self.head.next.next
                self.head.next.prev = self.head
            newNode = ListNode(key, value)
            self.node_dict[key] = newNode
            newNode.prev = self.tail.prev
            newNode.next = self.tail
            self.tail.prev.next = newNode
            self.tail.prev = newNode

LFU 缓存

力扣第460题
请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。
实现 LFUCache 类:
LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象
int get(int key) - 如果键 key 存在于缓存中,则获取键的值,否则返回 -1 。
void put(int key, int value) - 如果键 key 已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量 capacity 时,则应该在插入新项之前,移除最不经常使用的项。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近最久未使用 的键。
为了确定最不常使用的键,可以为缓存中的每个键维护一个 使用计数器 。使用计数最小的键是最久未使用的键。
当一个键首次插入到缓存中时,它的使用计数器被设置为 1 (由于 put 操作)。对缓存中的键执行 get 或 put 操作,使用计数器的值将会递增。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

'''
方法一:频率dict存储结点 + "key-node"dict存储对应关系
时间复杂度:O(n)->频率dict中换为双向链表会变为O(1)
'''
class ListNode:
    def __init__(self, key=None, value=None):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None
        self.freq = 1

class LFUCache:

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.node_dict = dict()
        self.node_freq = dict()
        self.min_freq = 1

    def update(self, key: int):
        node = self.node_dict[key]
        freq = node.freq
        for idx, n in enumerate(self.node_freq[freq]):
            if n is node:
                self.node_freq[freq].pop(idx)
        if self.min_freq == freq and not self.node_freq[freq]:
            self.min_freq += 1
        node.freq += 1
        freq = node.freq
        if not self.node_freq.get(freq):
            self.node_freq[freq] = list()
        self.node_freq[freq].append(node)

    def get(self, key: int) -> int:
        if key in self.node_dict:
            self.update(key)
            return self.node_dict[key].value
        return -1

    def put(self, key: int, value: int) -> None:
        if key in self.node_dict:
            self.node_dict[key].value = value
            self.update(key)
        else:
            if len(self.node_dict) == self.capacity:
                node = self.node_freq[self.min_freq][0]
                self.node_freq[self.min_freq].pop(0)
                self.node_dict.pop(node.key)

            newNode = ListNode(key, value)
            self.node_dict[key] = newNode
            if not self.node_freq.get(1):
                self.node_freq[1] = list()
            self.node_freq[1].append(newNode)
            self.min_freq = 1
'''
方法二:频率dict[双向链表]存储结点 + "key-node"dict存储对应关系
时间复杂度:O(1)
'''
class ListNode:
    def __init__(self, key=None, value=None):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None
        self.freq = 1

class DLinkList:
    def __init__(self):
        self.sentinel = ListNode(None, None)
        self.sentinel.next = self.sentinel.prev = self.sentinel
    
    def append(self, node: ListNode):
        node.next = self.sentinel
        node.prev = self.sentinel.prev
        node.next.prev = node
        node.prev.next = node
    
    def pop(self, node=None):
        if node is not None:
            node.prev.next = node.next
            node.next.prev = node.prev
        else:
            node = self.sentinel.next
            self.sentinel.next = node.next
            self.sentinel.next.prev = self.sentinel
        return node
            
    def isEmpty(self):
        return self.sentinel.next == self.sentinel

class LFUCache:

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.node_dict = dict()
        self.node_freq = dict()
        self.min_freq = 1

    def update(self, node: ListNode):
        freq = node.freq
        self.node_freq[freq].pop(node)
        if self.min_freq == freq and self.node_freq[freq].isEmpty():
            self.min_freq += 1
        node.freq += 1
        freq = node.freq
        if not self.node_freq.get(freq):
            self.node_freq[freq] = DLinkList()
        self.node_freq[freq].append(node)

    def get(self, key: int) -> int:
        if key in self.node_dict:
            self.update(self.node_dict[key])
            return self.node_dict[key].value
        return -1

    def put(self, key: int, value: int) -> None:
        if key in self.node_dict:
            self.node_dict[key].value = value
            self.update(self.node_dict[key])
        else:
            if len(self.node_dict) == self.capacity:
                node = self.node_freq[self.min_freq].pop()
                self.node_dict.pop(node.key)

            newNode = ListNode(key, value)
            self.node_dict[key] = newNode
            if not self.node_freq.get(1):
                self.node_freq[1] = DLinkList()
            self.node_freq[1].append(newNode)
            self.min_freq = 1

设计跳表

力扣第1206题
跳表 是在 O(log(n)) 时间内完成增加、删除、搜索操作的数据结构。跳表相比于树堆与红黑树,其功能与性能相当,并且跳表的代码长度相较下更短,其设计思想与链表相似。
在本题中,你的设计应该要包含这些函数:
bool search(int target) : 返回target是否存在于跳表中。
void add(int num): 插入一个元素到跳表。
bool erase(int num): 在跳表中删除一个值,如果 num 不存在,直接返回false. 如果存在多个 num ,删除其中任意一个即可。
注意,跳表中可能存在多个相同的值,你的代码需要处理这种情况。

'''
方法一:链表 + 随机数
时间复杂度:O(logn)
空间复杂度:O(n)
'''
import random
class ListNode:
    def __init__(self, data:int=None):
        self.data = data
        self.forwards = list() # 存储各个索引层级中该节点的后驱索引节点

class Skiplist:

    def __init__(self):
        self.level_count = 1 # 初始化当前层级为1
        self.head = ListNode()
        self.max_level = 16 # 允许的最大索引高度
        self.head.forwards = [None] * self.max_level 

    def random_level(self, p:float=0.5) -> int:
        level = 1
        while random.random() < p and level < self.max_level:
            level += 1
        return level

    def search(self, target: int) -> bool:
        p = self.head
        # 从最高索引层级不断搜索,如果当前层级没有,则下沉到低一级的层级
        for i in range(self.level_count-1, -1, -1):
            while p.forwards[i] and p.forwards[i].data < target:
                p = p.forwards[i]
        if p.forwards[0] and p.forwards[0].data == target:
            return True
        return False

    def add(self, num: int) -> None:
        level = self.random_level() # 随机生成索引层级
        # 如果当前层级小于level,则更新当前最高层级
        if self.level_count < level:
            self.level_count = level
        new_node = ListNode(num) # 生成新节点
        new_node.forwards = [None] * level
        # 用来保存各个索引层级插入的位置,也就是新节点的前驱节点
        update = [self.head] * self.level_count
        # 获取新插入节点在各个索引层级的前驱节点
        p = self.head
        for i in range(self.level_count-1, -1, -1):
            while p.forwards[i] and p.forwards[i].data < num:
                p = p.forwards[i]
            update[i] = p
        # 更新需要更新的各个索引层级
        for i in range(level):
            new_node.forwards[i] = update[i].forwards[i]
            update[i].forwards[i] = new_node

    def erase(self, num: int) -> bool:
        update = [None] * self.level_count
        p = self.head
        for i in range(self.level_count-1, -1, -1):
            while p.forwards[i] and p.forwards[i].data < num:
                p = p.forwards[i]
            update[i] = p
        
        if p.forwards[0] and p.forwards[0].data == num:
            for i in range(self.level_count-1, -1, -1):
                if update[i].forwards[i] and update[i].forwards[i].data == num:
                    update[i].forwards[i] = update[i].forwards[i].forwards[i]
            return True
        while self.level_count > 1 and not self.head.forwards[self.level_count]:
            self.level_count -= 1
        return False

笔记本-Github

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jiawen9

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值