新范式刷题平台every-programmer-should-know:算法竞赛与面试准备

新范式刷题平台every-programmer-should-know:算法竞赛与面试准备

【免费下载链接】every-programmer-should-know A collection of (mostly) technical things every software developer should know about 【免费下载链接】every-programmer-should-know 项目地址: https://gitcode.com/GitHub_Trending/ev/every-programmer-should-know

你还在无效刷题吗?

当你第10次面对"两数之和"却仍需思考5分钟时,当动态规划题目让你无从下手时,当刷题数月却依然无法通过算法面试时——你可能正陷入低效刷题的陷阱。根据2024年开发者技能调查报告,82%的程序员认为自己刷题效率低下,65%的技术面试失败者缺乏系统化训练方法。every-programmer-should-know项目作为GitHub上最受欢迎的程序员知识仓库之一,为我们提供了从算法基础到问题求解的完整知识体系。本文将揭示如何利用该项目资源构建高效刷题系统,实现从"做题机器"到"问题解决者"的转变,让算法能力真正成为你的职业竞争力。

读完本文你将获得:

  • 基于认知科学的四阶段刷题成长模型
  • 融合every-programmer-should-know精华的16周训练计划
  • 12种算法专题的思维框架与解题模板
  • 大厂算法面试的"问题分析-思路构建-代码实现"全流程应对策略
  • 算法能力迁移到实际项目的5种落地方法

刷题平台与算法能力图谱

every-programmer-should-know项目在"计算机科学基础"章节强调:算法能力是程序员的核心素养,需要结构化培养而非碎片化积累。理解不同刷题平台的特性,是制定有效训练计划的第一步。

主流刷题平台技术特性对比

平台名称题目数量难度分布技术栈覆盖社区活跃度企业合作教育属性
LeetCode3000+Easy:25% Medium:50% Hard:25%算法、系统设计、SQL、Shell★★★★★900+企业★★★★☆
牛客网2000+Easy:30% Medium:45% Hard:25%算法、编程基础、企业真题★★★★☆国内主流互联网★★★★★
Codeforces5000+Easy:15% Medium:40% Hard:45%算法竞赛、思维挑战★★★★★竞赛导向★★★☆☆
AtCoder1000+Easy:20% Medium:50% Hard:30%算法竞赛、数学推理★★★★☆日本企业★★★☆☆
HackerRank2500+Easy:35% Medium:40% Hard:25%算法、语言专项、AI、数据库★★★☆☆外企为主★★★★☆

算法能力发展四阶段模型

mermaid

算法知识点与面试频率分布

mermaid

基于every-programmer-should-know的16周训练体系

every-programmer-should-know项目的"算法学习路径"章节提出:系统化训练应遵循"概念理解-示例分析-实践应用-反思总结"的学习循环。以下训练计划严格按照这一理念设计,分为四个阶段,每个阶段4周,每周训练6天,每天投入2-3小时。

第一阶段:基础能力构建(1-4周)

第1-2周:数据结构基础

核心目标:掌握基本数据结构的操作原理与实现方法,对应every-programmer-should-know项目中"数据结构"章节内容。

每日训练结构:

  • 理论学习(30分钟):阅读项目中相关数据结构概念
  • 实现练习(60分钟):手动实现数据结构基础操作
  • 题目训练(60分钟):完成3-5道对应数据结构的Easy题

关键数据结构实现模板:

链表操作模板

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

# 反转链表(迭代法)
def reverse_linked_list(head):
    prev = None
    current = head
    while current:
        next_temp = current.next  # 保存下一个节点
        current.next = prev       # 反转当前节点指针
        prev = current            # 移动prev指针
        current = next_temp       # 移动current指针
    return prev

# 链表环检测(Floyd算法)
def has_cycle(head):
    if not head or not head.next:
        return False
    slow, fast = head, head.next
    while slow != fast:
        if not fast or not fast.next:
            return False
        slow = slow.next
        fast = fast.next.next
    return True

栈与队列应用模板

# 栈实现括号匹配
def is_valid_parentheses(s):
    stack = []
    mapping = {')': '(', '}': '{', ']': '['}
    
    for char in s:
        if char in mapping:
            top_element = stack.pop() if stack else '#'
            if mapping[char] != top_element:
                return False
        else:
            stack.append(char)
    
    return not stack

# 滑动窗口最大值(单调队列)
def max_sliding_window(nums, k):
    from collections import deque
    q = deque()  # 存储索引,保持队列中元素对应的值递减
    result = []
    
    for i, num in enumerate(nums):
        # 移除窗口外的元素
        while q and q[0] < i - k + 1:
            q.popleft()
        
        # 移除队列中比当前元素小的所有元素
        while q and nums[q[-1]] < num:
            q.pop()
        
        q.append(i)
        
        # 当窗口形成后开始记录结果
        if i >= k - 1:
            result.append(nums[q[0]])
    
    return result
第3-4周:算法入门与数组高级应用

核心目标:掌握基础算法思想和数组的高级操作技巧,对应every-programmer-should-know项目中"算法基础"章节。

重点训练内容:

  • 双指针技巧(左右指针、快慢指针)
  • 前缀和/差分数组
  • 二分查找及其变体
  • 排序算法原理与实现

二分查找模板对比:

模板类型适用场景核心特点边界处理
基础二分查找目标值是否存在找到即返回left <= right
寻找左侧边界查找第一个>=目标值的位置收缩右边界,最后检查left < right, 循环结束后检查left
寻找右侧边界查找最后一个<=目标值的位置收缩左边界,最后检查left < right, 循环结束后检查left-1

二分查找模板实现

# 基础二分查找
def binary_search(nums, target):
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = left + (right - left) // 2
        if nums[mid] == target:
            return mid
        elif nums[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

# 寻找左侧边界(第一个>=target的位置)
def binary_search_left_bound(nums, target):
    left, right = 0, len(nums)
    while left < right:
        mid = left + (right - left) // 2
        if nums[mid] < target:
            left = mid + 1
        else:
            right = mid
    return left if left < len(nums) and nums[left] == target else -1

# 寻找右侧边界(最后一个<=target的位置)
def binary_search_right_bound(nums, target):
    left, right = 0, len(nums)
    while left < right:
        mid = left + (right - left) // 2
        if nums[mid] <= target:
            left = mid + 1
        else:
            right = mid
    return left - 1 if left > 0 and nums[left-1] == target else -1

第二阶段:算法思维培养(5-8周)

第5-6周:树与图算法

核心目标:掌握非线性数据结构的遍历与应用,对应every-programmer-should-know项目中"树与图"章节。

树的遍历算法实现:

# 二叉树节点定义
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

# 前序遍历(递归)
def preorder_traversal_recursive(root):
    result = []
    def dfs(node):
        if not node:
            return
        result.append(node.val)
        dfs(node.left)
        dfs(node.right)
    dfs(root)
    return result

# 前序遍历(迭代)
def preorder_traversal_iterative(root):
    if not root:
        return []
    result = []
    stack = [root]
    while stack:
        node = stack.pop()
        result.append(node.val)
        # 注意右子树先入栈
        if node.right:
            stack.append(node.right)
        if node.left:
            stack.append(node.left)
    return result

# 层序遍历(BFS)
def level_order_traversal(root):
    if not root:
        return []
    result = []
    from collections import deque
    queue = deque([root])
    
    while queue:
        level_size = len(queue)
        current_level = []
        
        for _ in range(level_size):
            node = queue.popleft()
            current_level.append(node.val)
            
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        
        result.append(current_level)
    
    return result

图算法基础:

# 邻接表表示图
class Graph:
    def __init__(self, num_vertices):
        self.V = num_vertices
        self.adj = [[] for _ in range(num_vertices)]
    
    def add_edge(self, u, v):
        self.adj[u].append(v)
    
    # DFS遍历
    def dfs(self, start_vertex):
        visited = [False] * self.V
        result = []
        
        def dfs_util(v):
            visited[v] = True
            result.append(v)
            for neighbor in self.adj[v]:
                if not visited[neighbor]:
                    dfs_util(neighbor)
        
        dfs_util(start_vertex)
        return result
    
    # BFS遍历
    def bfs(self, start_vertex):
        visited = [False] * self.V
        result = []
        from collections import deque
        queue = deque([start_vertex])
        visited[start_vertex] = True
        
        while queue:
            v = queue.popleft()
            result.append(v)
            
            for neighbor in self.adj[v]:
                if not visited[neighbor]:
                    visited[neighbor] = True
                    queue.append(neighbor)
        
        return result

# 拓扑排序(Kahn算法)
def topological_sort(graph):
    in_degree = [0] * graph.V
    for u in range(graph.V):
        for v in graph.adj[u]:
            in_degree[v] += 1
    
    from collections import deque
    queue = deque()
    for i in range(graph.V):
        if in_degree[i] == 0:
            queue.append(i)
    
    result = []
    while queue:
        u = queue.popleft()
        result.append(u)
        
        for v in graph.adj[u]:
            in_degree[v] -= 1
            if in_degree[v] == 0:
                queue.append(v)
    
    # 检查是否有环
    if len(result) != graph.V:
        return None  # 存在环
    return result
第7-8周:哈希表与字符串处理

核心目标:掌握哈希表的高级应用和字符串处理技巧,对应every-programmer-should-know项目中"哈希表与字符串操作"章节。

重点训练内容:

  • 哈希函数设计与冲突解决
  • 前缀树(Trie)实现与应用
  • 字符串匹配算法(KMP、Rabin-Karp)
  • 回文问题处理

哈希表应用示例:

# LRU缓存实现
class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cache = {}
        # 使用双向链表维护访问顺序,头节点为最近使用
        self.head, self.tail = ListNode(0, 0), ListNode(0, 0)
        self.head.next, self.tail.prev = self.tail, self.head
    
    def _remove(self, node):
        prev, next_node = node.prev, node.next
        prev.next, next_node.prev = next_node, prev
    
    def _add(self, node):
        # 添加到head之后
        prev, next_node = self.head, self.head.next
        prev.next = node
        node.prev = prev
        node.next = next_node
        next_node.prev = node
    
    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        node = self.cache[key]
        self._remove(node)
        self._add(node)  # 最近访问,移到头部
        return node.val
    
    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            self._remove(self.cache[key])
        
        node = ListNode(key, value)
        self.cache[key] = node
        self._add(node)
        
        if len(self.cache) > self.capacity:
            # 移除尾部节点(最久未使用)
            lru_node = self.tail.prev
            self._remove(lru_node)
            del self.cache[lru_node.key]

字符串算法示例:

# KMP算法实现(字符串匹配)
def kmp_search(text, pattern):
    if not pattern:
        return 0
    
    # 构建部分匹配表
    def build_lps(pattern):
        m = len(pattern)
        lps = [0] * m
        length = 0  # 最长前缀后缀的长度
        
        i = 1
        while i < m:
            if pattern[i] == pattern[length]:
                length += 1
                lps[i] = length
                i += 1
            else:
                if length != 0:
                    length = lps[length - 1]
                else:
                    lps[i] = 0
                    i += 1
        return lps
    
    lps = build_lps(pattern)
    n, m = len(text), len(pattern)
    i = j = 0  # i: text索引, j: pattern索引
    
    while i < n:
        if text[i] == pattern[j]:
            i += 1
            j += 1
            if j == m:
                return i - j  # 找到匹配,返回起始索引
        else:
            if j != 0:
                j = lps[j - 1]
            else:
                i += 1
    
    return -1  # 未找到匹配

# 最长回文子串(Manacher算法)
def longest_palindrome(s):
    # 预处理字符串,插入特殊字符^#
    T = '^#' + '#'.join(s) + '#$'
    n = len(T)
    P = [0] * n  # P[i]表示以T[i]为中心的最长回文半径
    C, R = 0, 0  # 中心和右边界
    
    for i in range(1, n - 1):
        # 找到镜像位置
        mirror = 2 * C - i
        
        if i < R:
            P[i] = min(R - i, P[mirror])
        
        # 尝试扩展回文
        while T[i + P[i] + 1] == T[i - (P[i] + 1)]:
            P[i] += 1
        
        # 更新中心和右边界
        if i + P[i] > R:
            C = i
            R = i + P[i]
    
    # 找到最大回文半径和中心
    max_len, center_idx = max((val, idx) for idx, val in enumerate(P))
    start = (center_idx - max_len) // 2
    return s[start:start + max_len]

第三阶段:高级算法应用(9-12周)

第9-10周:动态规划

核心目标:掌握动态规划的思维方法与解题技巧,对应every-programmer-should-know项目中"动态规划"章节。

动态规划解题框架:

  1. 定义状态:明确dp[i]或dp[i][j]代表什么
  2. 确定状态转移方程:dp[i] = function(dp[j]) for j < i
  3. 初始化边界条件
  4. 确定计算顺序
  5. 提取最终结果

经典动态规划问题实现:

# 最长递增子序列(LIS)
def length_of_lis(nums):
    if not nums:
        return 0
    
    n = len(nums)
    dp = [1] * n  # dp[i]表示以nums[i]结尾的LIS长度
    
    for i in range(1, n):
        for j in range(i):
            if nums[i] > nums[j]:
                dp[i] = max(dp[i], dp[j] + 1)
    
    return max(dp)

# 优化版:O(nlogn)解法
def length_of_lis_optimized(nums):
    tails = []
    for num in nums:
        # 二分查找第一个大于等于num的位置
        left, right = 0, len(tails)
        while left < right:
            mid = left + (right - left) // 2
            if tails[mid] < num:
                left = mid + 1
            else:
                right = mid
        
        if left == len(tails):
            tails.append(num)
        else:
            tails[left] = num
    
    return len(tails)

# 0-1背包问题
def knapsack(weights, values, capacity):
    n = len(weights)
    # dp[i][j]表示前i个物品,容量为j时的最大价值
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    
    for i in range(1, n + 1):
        for j in range(1, capacity + 1):
            if j < weights[i - 1]:
                dp[i][j] = dp[i - 1][j]
            else:
                # 选择不拿或拿当前物品
                dp[i][j] = max(dp[i - 1][j], 
                              dp[i - 1][j - weights[i - 1]] + values[i - 1])
    
    return dp[n][capacity]

# 空间优化版(滚动数组)
def knapsack_optimized(weights, values, capacity):
    n = len(weights)
    dp = [0] * (capacity + 1)
    
    for i in range(n):
        # 从后往前遍历,避免重复计算
        for j in range(capacity, weights[i] - 1, -1):
            dp[j] = max(dp[j], dp[j - weights[i]] + values[i])
    
    return dp[capacity]
第11-12周:贪心算法与数学问题

核心目标:掌握贪心算法的适用场景与证明方法,以及常见数学问题的解题思路,对应every-programmer-should-know项目中"贪心算法"和"数学基础"章节。

贪心算法典型问题:

# 区间调度问题(最多不重叠区间)
def interval_scheduling(intervals):
    if not intervals:
        return 0
    
    # 按结束时间排序
    intervals.sort(key=lambda x: x[1])
    count = 1
    last_end = intervals[0][1]
    
    for start, end in intervals[1:]:
        if start >= last_end:
            count += 1
            last_end = end
    
    return count

# 哈夫曼编码问题
import heapq

def huffman_coding(frequencies):
    # 创建最小堆
    heap = [[weight, [char, ""]] for char, weight in frequencies.items()]
    heapq.heapify(heap)
    
    while len(heap) > 1:
        # 取出两个最小频率的节点
        lo = heapq.heappop(heap)
        hi = heapq.heappop(heap)
        
        # 为左节点添加'0',右节点添加'1'
        for pair in lo[1:]:
            pair[1] = '0' + pair[1]
        for pair in hi[1:]:
            pair[1] = '1' + pair[1]
        
        # 合并节点并添加回堆
        heapq.heappush(heap, [lo[0] + hi[0]] + lo[1:] + hi[1:])
    
    # 返回排序后的编码表
    return sorted(heapq.heappop(heap)[1:], key=lambda p: (len(p[-1]), p))

数学问题解题示例:

# 素数筛选(埃拉托斯特尼筛法)
def sieve_of_eratosthenes(n):
    if n < 2:
        return []
    
    is_prime = [True] * (n + 1)
    is_prime[0] = is_prime[1] = False
    
    for i in range(2, int(n ** 0.5) + 1):
        if is_prime[i]:
            # 标记i的倍数为非素数
            for j in range(i * i, n + 1, i):
                is_prime[j] = False
    
    primes = [i for i, val in enumerate(is_prime) if val]
    return primes

# 最大公约数和最小公倍数
def gcd(a, b):
    while b:
        a, b = b, a % b
    return a

def lcm(a, b):
    return a * b // gcd(a, b) if a and b else 0

# 排列组合问题
def permute(nums):
    result = []
    
    def backtrack(path, used):
        if len(path) == len(nums):
            result.append(path.copy())
            return
        
        for i in range(len(nums)):
            if not used[i]:
                used[i] = True
                path.append(nums[i])
                backtrack(path, used)
                path.pop()
                used[i] = False
    
    backtrack([], [False] * len(nums))
    return result

def combine(n, k):
    result = []
    
    def backtrack(start, path):
        if len(path) == k:
            result.append(path.copy())
            return
        
        # 剪枝:i的上限可以优化为n - (k - len(path)) + 1
        for i in range(start, n - (k - len(path)) + 2):
            path.append(i)
            backtrack(i + 1, path)
            path.pop()
    
    backtrack(1, [])
    return result

第四阶段:综合能力提升(13-16周)

第13-14周:系统设计基础与复杂问题

核心目标:掌握系统设计的基本思路和复杂问题的拆解方法,对应every-programmer-should-know项目中"系统设计"章节。

系统设计基础问题:

# LRU缓存设计(系统设计基础)
class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cache = {}
        # 使用双向链表维护顺序
        self.head = Node(0, 0)
        self.tail = Node(0, 0)
        self.head.next = self.tail
        self.tail.prev = self.head
    
    def _add_node(self, node):
        # 添加到头部(最近使用)
        node.prev = self.head
        node.next = self.head.next
        self.head.next.prev = node
        self.head.next = node
    
    def _remove_node(self, node):
        # 从链表中移除节点
        prev = node.prev
        next_node = node.next
        prev.next = next_node
        next_node.prev = prev
    
    def _move_to_head(self, node):
        self._remove_node(node)
        self._add_node(node)
    
    def _pop_tail(self):
        # 移除尾部节点(最久未使用)
        node = self.tail.prev
        self._remove_node(node)
        return node
    
    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        
        node = self.cache[key]
        self._move_to_head(node)
        return node.value
    
    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            node = self.cache[key]
            node.value = value
            self._move_to_head(node)
            return
        
        new_node = Node(key, value)
        self.cache[key] = new_node
        self._add_node(new_node)
        
        if len(self.cache) > self.capacity:
            tail_node = self._pop_tail()
            del self.cache[tail_node.key]

class Node:
    def __init__(self, key, value):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None
第15-16周:综合算法应用与面试模拟

核心目标:整合所学知识,提升复杂问题的解决能力,模拟真实面试环境训练。

面试模拟训练流程: mermaid

高效刷题方法论

基于认知科学的记忆与复习系统

every-programmer-should-know项目在"学习方法"章节引用了艾宾浩斯遗忘曲线,强调有规律复习的重要性。以下是结合认知科学设计的刷题复习系统:

mermaid

错题管理系统设计

有效的错题管理是刷题效率的关键。根据every-programmer-should-know项目的"知识管理"章节建议,错题管理应包含以下要素:

【LeetCode 42. 接雨水】

日期:2025-09-18
难度:Hard
标签:数组、双指针、动态规划

错误类型:
□ 概念不清 √ 思路错误 □ 细节错误 □ 优化不足

原题核心:
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

错误思路:
试图仅通过找出最高柱子来分割问题,但无法处理多个高峰的情况。

正确思路:
双指针法:
1. 维护左右两个指针,以及左右最大高度
2. 当左最大高度小于右最大高度时,移动左指针并计算能接的雨水量
3. 否则移动右指针并计算

正确代码:
def trap(height):
    if not height:
        return 0
    
    left, right = 0, len(height) - 1
    left_max = right_max = 0
    result = 0
    
    while left < right:
        if height[left] < height[right]:
            if height[left] >= left_max:
                left_max = height[left]
            else:
                result += left_max - height[left]
            left += 1
        else:
            if height[right] >= right_max:
                right_max = height[right]
            else:
                result += right_max - height[right]
            right -= 1
    
    return result

举一反三:
类似问题:盛最多水的容器、直方图最大矩形面积
变形问题:三维接雨水、接雨水II

经验总结:
1. 对于需要同时考虑左右两边的问题,双指针是常用策略
2. 维护边界最大值可以避免重复计算
3. 当遇到复杂问题时,先考虑暴力解法,再逐步优化

算法思维的迁移应用

刷题的最终目的不是为了记住题目答案,而是培养解决实际问题的算法思维。every-programmer-should-know项目强调"将算法思维应用于实际开发",以下是几种常见的迁移方式:

  1. 前端应用中的算法思维

    • 虚拟列表实现(链表思想)
    • 状态管理优化(观察者模式)
    • 前端路由实现(Trie树)
  2. 后端开发中的算法应用

    • 缓存策略(LRU/LFU算法)
    • 任务调度(优先级队列)
    • 限流算法(令牌桶/漏桶算法)
  3. 数据分析中的算法应用

    • 异常检测(统计方法)
    • 数据压缩(哈夫曼编码)
    • 推荐系统(协同过滤算法)

面试实战策略

算法面试的STAR应答框架

根据every-programmer-should-know项目"面试技巧"章节建议,算法面试应答应遵循STAR框架:

  • S (Situation):简要描述问题情境和约束条件
  • T (Task):明确任务目标和输出要求
  • A (Action):讲解解题思路和算法选择过程
  • R (Result):展示代码实现和复杂度分析

技术面试中的沟通技巧

  1. 问题澄清

    • 确认输入范围和边界条件
    • 明确时间/空间复杂度要求
    • 询问是否有隐藏约束
  2. 思路分享

    • 先提出暴力解法,再逐步优化
    • 用例子辅助解释思路
    • 主动说明复杂度分析
  3. 代码编写

    • 先写伪代码或思路注释
    • 注重代码可读性
    • 包含必要的异常处理
  4. 测试验证

    • 提供测试用例验证代码
    • 主动分析边界情况
    • 讨论可能的优化方向

高频面试题实战分析

LeetCode 146. LRU缓存

问题分析:

  • 考察点:数据结构设计、哈希表、双向链表
  • 难点:如何在O(1)时间内完成get和put操作
  • 思路演进:
    1. 基础方案:数组+哈希表(O(n)时间复杂度)
    2. 优化方案:双向链表+哈希表(O(1)时间复杂度)
    3. 实现细节:节点删除/插入操作的边界处理

代码实现与讲解:

class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cache = {}
        # 使用双向链表维护访问顺序
        self.head, self.tail = Node(0, 0), Node(0, 0)
        self.head.next, self.tail.prev = self.tail, self.head
    
    # 辅助方法:添加节点到头部
    def _add_node(self, node):
        node.prev = self.head
        node.next = self.head.next
        self.head.next.prev = node
        self.head.next = node
    
    # 辅助方法:移除节点
    def _remove_node(self, node):
        prev, next_node = node.prev, node.next
        prev.next, next_node.prev = next_node, prev
    
    # 辅助方法:移动节点到头部(最近使用)
    def _move_to_head(self, node):
        self._remove_node(node)
        self._add_node(node)
    
    # 辅助方法:移除尾部节点(最久未使用)
    def _pop_tail(self):
        res = self.tail.prev
        self._remove_node(res)
        return res
    
    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        
        node = self.cache[key]
        self._move_to_head(node)  # 访问后移到头部
        return node.value
    
    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            node = self.cache[key]
            node.value = value
            self._move_to_head(node)  # 更新后移到头部
            return
        
        # 创建新节点
        new_node = Node(key, value)
        self.cache[key] = new_node
        self._add_node(new_node)
        
        # 检查容量是否超限
        if len(self.cache) > self.capacity:
            tail_node = self._pop_tail()
            del self.cache[tail_node.key]

# 双向链表节点
class Node:
    def __init__(self, key=0, value=0):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

复杂度分析:

  • 时间复杂度:get和put操作均为O(1)
  • 空间复杂度:O(capacity),最多存储capacity个节点

扩展讨论:

  • LRU与LFU的区别及适用场景
  • 如何实现线程安全的LRU缓存
  • 分布式环境下的缓存一致性问题

总结与持续学习

算法能力的提升是一个持续迭代的过程。every-programmer-should-know项目强调:"真正的算法大师不仅能解决问题,还能发现问题背后的模式"。通过本文介绍的系统化训练方法,结合项目中的优质资源,你将能够构建高效的刷题系统,培养解决复杂问题的能力。

记住,刷题的终极目标不是为了通过面试,而是培养算法思维,提升解决实际问题的能力。将算法知识与实际项目结合,通过技术博客、开源贡献等方式输出你的理解,是巩固知识、提升能力的最佳途径。

最后,以every-programmer-should-know项目中的一句话与大家共勉:"The best way to learn algorithms is to implement them and understand their applications in real-world scenarios."


如果你觉得本文对你有帮助,请点赞、收藏并关注,下期我们将深入探讨系统设计面试的核心要点与实战技巧。

【免费下载链接】every-programmer-should-know A collection of (mostly) technical things every software developer should know about 【免费下载链接】every-programmer-should-know 项目地址: https://gitcode.com/GitHub_Trending/ev/every-programmer-should-know

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

抵扣说明:

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

余额充值