news 2026/10/15 0:12:49

技术面试图论高频考点:从存储选型到Dijkstra实战避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
技术面试图论高频考点:从存储选型到Dijkstra实战避坑指南

1. 图结构在面试中的真实定位

聊到图这个数据结构,很多人的第一反应是"难"。难在哪?不是概念难,是面试官一旦往图的方向出题,往往意味着他想考察的东西比数组、链表、栈队列要深得多。数组和链表考的是基本功,图考的是你建模能力、算法选择能力、以及代码落地能力的综合体现。

我在过去几年帮不少人做过模拟面试,发现一个很普遍的现象:候选人能把DFS和BFS的伪代码背得滚瓜烂熟,但面试官给一个实际场景,比如"判断课程安排是否合理"或者"计算网络延迟时间",立刻就不知道从哪下手了。问题出在——他们学的是"图的算法",而不是"用图解决问题"。

这篇内容就是冲着这个痛点来的。我会把图在面试中真正高频出现的考点拆开,从建模思路到代码实现,从常见变形到踩坑经验,尽量讲透。适合正在准备技术面试的开发者,也适合那些工作中偶尔需要用到图算法但一直没系统整理过的朋友。不管你是刚学完数据结构的学生,还是工作几年后回头补算法的工程师,下面的内容应该都能帮你把图这块的知识串起来。

2. 图的存储选型:邻接矩阵还是邻接表

面试里关于图的第一道坎,往往不是算法本身,而是你怎么存这个图。很多人刷题的时候直接用邻接表一把梭,但面试官如果追问一句"你为什么不用邻接矩阵",答不上来就尴尬了。

2.1 两种存储方式的本质差异

邻接矩阵就是一个二维数组matrix[i][j],表示节点 i 到节点 j 是否有边(或者边的权重)。它的特点是查询任意两个节点之间是否有边,时间复杂度是 O(1),非常快。但代价是空间复杂度固定为 O(V²),V 是节点数量。如果图很稀疏,比如1000个节点只有2000条边,用邻接矩阵就要开100万个格子,其中绝大多数是浪费的。

邻接表则是每个节点维护一个列表,列表里存的是它能直接到达的邻居。空间复杂度是 O(V+E),E 是边的数量。查某条边是否存在需要遍历对应节点的邻居列表,最坏情况 O(V),但遍历某个节点的所有邻居非常高效。

我一般会这样跟人解释:邻接矩阵像一张完整的城市交通地图,任何两个地点之间有没有直达路线一眼就能查到,但地图本身很大;邻接表像每个人的通讯录,只记录自己认识的人,省空间,但想知道"张三认不认识李四"得翻张三的通讯录。

2.2 面试中的选型判断标准

选哪个,核心看两个维度:图的稠密程度和你要做的操作类型。

判断维度倾向邻接矩阵倾向邻接表
图的稠密程度稠密图(E接近V²)稀疏图(E远小于V²)
频繁操作判断两点是否相邻遍历某点的所有邻居
节点规模节点数较少(V≤1000)节点数较大
需要存权重矩阵直接存权重,直观列表元素需额外存权重
代码简洁度初始化简单需要动态结构

实际面试中,绝大多数题目给的图都是稀疏图,所以邻接表出现的频率更高。但有一种情况要特别注意:如果题目明确说节点数不超过100,而且需要频繁判断任意两点之间的连通性,那邻接矩阵反而更合适。

2.3 邻接表的代码实现细节

用Python写邻接表,最常见的有两种方式。一种是字典套列表:

# 方式一:字典 + 列表 graph = {} for u, v in edges: if u not in graph: graph[u] = [] if v not in graph: graph[v] = [] graph[u].append(v) graph[v].append(u) # 无向图需要反向也加

另一种是用defaultdict简化:

from collections import defaultdict graph = defaultdict(list) for u, v in edges: graph[u].append(v) graph[v].append(u)

如果边带权重,列表里存的就不是单个节点,而是(邻居, 权重)这样的元组:

graph = defaultdict(list) for u, v, w in edges: graph[u].append((v, w)) graph[v].append((u, w))

这里有个容易翻车的点:无向图一定要记得双向添加边。我见过不止一个候选人在面试时只加了一个方向,导致后面BFS遍历的时候漏掉一半节点,调试半天才发现。建议写完建图代码后,用一个小例子手动验证一下,比如三个节点的三角形,看看每个节点的邻居列表是否对称。

注意:如果题目中节点编号是0到n-1的连续整数,用列表套列表graph = [[] for _ in range(n)]会比字典更快,也避免了key不存在的判断。但如果节点编号是字符串或者不连续的整数,就必须用字典。

3. 深度优先搜索的递归陷阱与迭代写法

DFS是图算法里最基础的遍历方式,思路简单——一条路走到黑,走不通就回退。但就是这么一个简单的思路,在面试中能暴露出很多问题。

3.1 递归写法的三个隐藏坑

先看最经典的递归DFS模板:

def dfs(node, visited, graph): visited.add(node) # 处理当前节点 for neighbor in graph[node]: if neighbor not in visited: dfs(neighbor, visited, graph)

看起来没问题对吧?但面试中经常在这几个地方出问题。

第一个坑是忘记标记visited的时机。有些人习惯在遍历邻居的时候才标记,而不是进入函数就标记。这在树结构里没问题,但在图里会导致同一个节点被多次访问,严重时直接栈溢出。正确的做法是一进入DFS函数就立刻标记当前节点已访问。

第二个坑是递归深度超限。Python默认的递归深度限制是1000左右,如果图是一条长链,比如1000个节点依次相连,递归DFS直接报RecursionError。面试时如果数据规模不确定,要么改用迭代写法,要么手动sys.setrecursionlimit(100000)。但后者在面试中不一定被允许,所以迭代写法更稳妥。

第三个坑是在递归中修改共享状态。比如你想在DFS过程中记录路径,如果用全局列表append和pop,要注意回溯时清理干净。我见过有人忘记pop,导致路径越积越长,结果完全错误。

3.2 迭代写法的正确姿势

DFS的迭代写法用栈来模拟递归调用:

def dfs_iterative(start, graph): visited = set() stack = [start] while stack: node = stack.pop() if node in visited: continue visited.add(node) # 处理当前节点 for neighbor in graph[node]: if neighbor not in visited: stack.append(neighbor)

注意这里visited的判断放在了pop之后,而不是push之前。这两种写法的区别在于:push之前判断可以避免重复入栈,节省空间;pop之后判断代码更简洁,但可能有重复元素在栈里。两种都能用,但面试时建议用push之前判断的版本,显得你对细节有把控。

还有一个细节:如果题目要求按某种顺序遍历(比如字典序最小的路径),用栈的迭代写法需要注意入栈顺序。因为栈是后进先出,你想要的顺序可能需要反着入栈。这个点很多人第一次遇到会懵,实际写一次就记住了。

3.3 DFS在面试题中的典型变形

DFS很少单独考"遍历一个图",它通常藏在具体问题里。几个高频变形:

岛屿数量问题:给一个二维网格,1代表陆地,0代表水,数有多少个岛屿。本质就是求连通分量个数,每个未访问的陆地节点做一次DFS,把相连的陆地全部标记为已访问。

路径记录问题:比如找出所有从起点到终点的路径。这时候DFS需要额外维护一个当前路径列表,进入节点时append,离开时pop,到达终点时把路径拷贝一份存入结果。

拓扑排序的DFS实现:在DFS完成一个节点的所有邻居后,把该节点加入结果列表的前端(或者用栈),最终得到的就是拓扑序。这个后面会详细讲。

4. 广度优先搜索与最短路径的天然联系

BFS和DFS最大的区别在于遍历顺序。DFS是一条路走到黑,BFS是一层一层往外扩。这个特性决定了BFS在"最短"类问题上有着天然优势。

4.1 为什么BFS能求最短路径

在无权图(或者边权都相同)中,BFS第一次访问到某个节点时,走过的步数一定是最少的。原因很直观:BFS像水波纹一样从起点向外扩散,每一层代表距离起点多一步。当你第一次碰到目标节点时,你正处于离起点最近的那一层,不可能有更短的路径了。

这个性质是BFS求最短路径的理论基础。面试中只要看到"最少步数""最短距离""最近"这类字眼,而且图是无权的,第一反应就应该是BFS。

BFS的标准模板:

from collections import deque def bfs(start, graph): visited = set([start]) queue = deque([start]) distance = {start: 0} while queue: node = queue.popleft() for neighbor in graph[node]: if neighbor not in visited: visited.add(neighbor) queue.append(neighbor) distance[neighbor] = distance[node] + 1 return distance

这里用deque而不是普通列表,是因为popleft()的时间复杂度是O(1),而列表的pop(0)是O(n)。数据量大的时候这个差异非常明显。

4.2 BFS的层序处理技巧

有些题目需要你区分每一层,比如"按层输出节点"或者"计算每一层的最小值"。有两种常见写法:

第一种是记录每层的大小:

while queue: level_size = len(queue) current_level = [] for _ in range(level_size): node = queue.popleft() current_level.append(node) for neighbor in graph[node]: if neighbor not in visited: visited.add(neighbor) queue.append(neighbor) # 处理current_level

第二种是用哨兵节点或者双队列,但第一种最直观,面试时推荐用。

4.3 多源BFS:一个容易被忽略的变种

普通BFS从一个起点出发,多源BFS从多个起点同时出发。典型题目是"腐烂的橘子"——网格里有多个腐烂的橘子,每轮腐烂相邻的新鲜橘子,问多少轮全部腐烂。

多源BFS的实现只需要在初始化队列时把所有起点都放进去,后面的逻辑和普通BFS完全一样。这个技巧在面试中出现的频率不低,但很多人第一次遇到时想不到,会试图对每个起点分别做BFS然后取最小值,那样复杂度就上去了。

5. 拓扑排序:从课程表问题说起

拓扑排序是图论面试中的高频考点,它的应用场景非常明确:一堆任务之间有依赖关系,需要确定一个合理的执行顺序。

5.1 拓扑排序的现实类比

最经典的例子就是课程安排。假设你要修完若干门课,有些课有先修要求,比如"数据结构"必须在"算法"之前修。把这些课程和依赖关系画成图,就是一个有向无环图(DAG)。拓扑排序就是找出一个学习顺序,使得每门课的先修课都排在它前面。

如果这个图里有环,比如A依赖B,B依赖C,C又依赖A,那就无解了。所以拓扑排序的另一个作用就是检测有向图中是否有环。

5.2 Kahn算法:基于入度的BFS方案

Kahn算法的思路很清晰:

  1. 统计每个节点的入度(有多少条边指向它)
  2. 把所有入度为0的节点加入队列
  3. 从队列取出节点,加入结果序列,同时把它所有邻居的入度减1
  4. 如果某个邻居的入度变成0,加入队列
  5. 重复直到队列为空
  6. 如果结果序列的长度等于节点总数,说明无环;否则有环
from collections import deque, defaultdict def topological_sort(n, prerequisites): graph = defaultdict(list) indegree = [0] * n for course, prereq in prerequisites: graph[prereq].append(course) indegree[course] += 1 queue = deque([i for i in range(n) if indegree[i] == 0]) result = [] while queue: node = queue.popleft() result.append(node) for neighbor in graph[node]: indegree[neighbor] -= 1 if indegree[neighbor] == 0: queue.append(neighbor) return result if len(result) == n else []

这个算法的时间复杂度是O(V+E),空间复杂度也是O(V+E),非常高效。

5.3 DFS实现拓扑排序的细节

DFS也能做拓扑排序,核心思路是:在DFS完成一个节点的所有后代访问后,把这个节点加到结果的前面。最终结果就是拓扑序。

def topological_sort_dfs(n, prerequisites): graph = defaultdict(list) for course, prereq in prerequisites: graph[prereq].append(course) visited = [0] * n # 0未访问 1访问中 2已完成 result = [] has_cycle = False def dfs(node): nonlocal has_cycle if has_cycle: return visited[node] = 1 for neighbor in graph[node]: if visited[neighbor] == 1: has_cycle = True return if visited[neighbor] == 0: dfs(neighbor) visited[node] = 2 result.append(node) for i in range(n): if visited[i] == 0: dfs(i) if has_cycle: return [] return result[::-1]

这里用三种状态标记节点:0表示没访问过,1表示正在访问(在当前DFS路径上),2表示已完成。如果在DFS过程中遇到状态为1的节点,说明找到了一个环。

提示:Kahn算法和DFS算法在面试中都可以用,但Kahn算法更直观,不容易写错。如果面试官没有特别要求,优先用Kahn算法。DFS版本的好处是可以在同一趟遍历中顺便检测环,代码更紧凑。

6. 并查集:解决连通性问题的利器

并查集(Union-Find)严格来说不算图算法,但它在图相关问题中出现的频率极高,尤其是涉及连通分量、环检测、动态连通性的场景。

6.1 并查集的核心操作与优化

并查集维护一组不相交的集合,支持两种操作:

  • find(x):查找x属于哪个集合(返回代表元素)
  • union(x, y):把x和y所在的集合合并

最朴素的实现就是一个数组parent,parent[i]指向i的父节点。find就是一路往上找根,union就是把一个根指向另一个根。

但朴素实现在极端情况下会退化成链表,find变成O(n)。所以需要两个优化:

路径压缩:在find的过程中,把沿途所有节点直接指向根节点。这样下次再find这些节点就是O(1)了。

按秩合并:union的时候,把矮的树接到高的树上,避免树越来越高。

class UnionFind: def __init__(self, n): self.parent = list(range(n)) self.rank = [0] * n self.count = n # 连通分量个数 def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) # 路径压缩 return self.parent[x] def union(self, x, y): root_x = self.find(x) root_y = self.find(y) if root_x == root_y: return False # 已经在同一集合,说明这条边构成了环 if self.rank[root_x] < self.rank[root_y]: root_x, root_y = root_y, root_x self.parent[root_y] = root_x if self.rank[root_x] == self.rank[root_y]: self.rank[root_x] += 1 self.count -= 1 return True

加上这两个优化后,并查集的操作均摊时间复杂度接近O(1),非常高效。

6.2 并查集在图问题中的典型应用

判断无向图是否有环:遍历所有边,对每条边的两个端点做union。如果某条边的两个端点已经在同一集合中,说明这条边构成了环。

计算连通分量个数:初始化时每个节点自成一个集合,每union一次连通分量减1。最终count就是连通分量个数。

Kruskal最小生成树:把所有边按权重排序,依次尝试加入,如果加入后不构成环(用并查集判断),就保留这条边。最终得到的就是最小生成树。

这里要区分一下:并查集判断环只适用于无向图。有向图的环检测要用拓扑排序或者DFS三色标记。

7. 最短路径算法:Dijkstra与它的边界

最短路径是图论中最实用的问题之一,面试中主要考察Dijkstra算法。但很多人对Dijkstra的理解停留在"用优先队列每次取最小的",对其适用条件和边界情况不够清楚。

7.1 Dijkstra的核心思想与实现

Dijkstra解决的是非负权图中单源最短路径问题。核心思想是贪心:每次从尚未确定的节点中选出距离起点最近的,确定它的最短距离,然后用它去松弛(更新)它的邻居。

import heapq def dijkstra(start, graph, n): dist = [float('inf')] * n dist[start] = 0 pq = [(0, start)] # (距离, 节点) while pq: d, node = heapq.heappop(pq) if d > dist[node]: continue # 过期条目,跳过 for neighbor, weight in graph[node]: new_dist = d + weight if new_dist < dist[neighbor]: dist[neighbor] = new_dist heapq.heappush(pq, (new_dist, neighbor)) return dist

这段代码里有一个关键细节:if d > dist[node]: continue。因为同一个节点可能被多次加入优先队列(每次距离更新都会加一次),但只有距离最小的那次是有效的。这个判断避免了重复处理,是Dijkstra实现中容易忽略的优化。

7.2 为什么Dijkstra不能处理负权边

Dijkstra的贪心策略建立在"当前最近的节点距离不会再被更新"这个假设上。但如果存在负权边,这个假设就不成立了——一个已经确定的节点,可能通过后面发现的负权边获得更短的距离。

举个例子:A到B距离1,A到C距离5,B到C距离-10。Dijkstra会先确定B的距离为1,然后确定C的距离为5。但实际上A到C可以通过A→B→C,距离是1+(-10)=-9,比5短得多。Dijkstra就错了。

有负权边的情况要用Bellman-Ford算法,时间复杂度O(VE),比Dijkstra的O((V+E)logV)要慢。面试中如果题目明确说边权非负,用Dijkstra;如果没说或者有负权,要主动提Bellman-Ford。

7.3 Dijkstra的常见变形题

网络延迟时间:给一个有向带权图,求从某个节点出发,所有节点都收到信号的最短时间。本质就是Dijkstra求单源最短路径,然后取最大值。如果有的节点不可达,返回-1。

概率最大的路径:每条边有一个概率值,求从起点到终点概率最大的路径。这个可以把Dijkstra的"距离最小"改成"概率最大",松弛条件从new_dist < dist[neighbor]改成new_prob > prob[neighbor]。优先队列改成最大堆(存负数或者用自定义比较)。

最小体力消耗路径:二维网格中从左上到右下,体力消耗是路径上相邻格子高度差的最大值。这个题的关键是重新定义"路径长度"——不是求和,而是取最大值。Dijkstra的框架不变,只是松弛条件变成new_cost = max(d, abs(height_diff))。

这些变形题的共同点是:Dijkstra的框架不变,变的是"距离"的定义和松弛的方式。理解了这一点,遇到新题就不会慌。

8. 面试实战中的避坑经验

前面讲的都是知识点,这一节聊点实在的——面试中图相关题目容易翻车的地方,以及我观察到的一些规律。

8.1 建模阶段最容易犯的错

很多人拿到题目后急于写代码,结果建图就建错了。几个典型问题:

节点编号不连续:题目给的节点可能是字符串,或者编号是1到n而不是0到n-1。如果直接用列表存图,要么越界要么漏节点。稳妥的做法是先用一个映射把节点编号转成0到n-1的连续整数。

有向图和无向图搞混:题目说"互相认识"是无向图,"A关注B"是有向图。建图时无向图要双向加边,有向图只加一个方向。这个错误看起来低级,但面试紧张时真的容易犯。

边的权重理解错误:有些题目中权重在节点上而不是边上,比如"每个城市有一个过路费"。这时候要么把节点权重转化到边上,要么在松弛时额外加上节点权重。需要仔细读题。

8.2 复杂度分析的常见误区

面试官经常会问"你的算法时间复杂度是多少"。图问题的复杂度分析要注意:

  • BFS/DFS遍历:O(V+E),不是O(V²)。很多人会漏掉E。
  • Dijkstra用优先队列:O((V+E)logV),不是O(VlogV)。因为每条边都可能触发一次入堆。
  • 邻接矩阵上的BFS/DFS:O(V²),因为要遍历整个矩阵。
  • 并查集:接近O(1)均摊,但严格说是O(α(V)),α是阿克曼函数的反函数,增长极慢。

分析的时候要把V和E都考虑进去,不要只盯着节点数。

8.3 代码实现中的高频bug

根据我帮人做模拟面试的经验,图相关代码的bug集中在几个地方:

bug类型具体表现修复方法
visited标记时机节点被重复访问入队/入栈时就标记
无向图单向加边遍历漏节点双向都加
优先队列过期条目重复处理节点加if d > dist[node]: continue
递归深度超限RecursionError改迭代或调高限制
并查集忘记路径压缩超时find中递归压缩
拓扑排序入度更新遗漏结果不完整每个邻居都要减入度

这些bug单独看都不难,但在面试的紧张环境下,很容易顾此失彼。建议平时练习时就有意识地检查这几个点,形成肌肉记忆。

8.4 面试沟通中的加分项

图问题的面试,代码只是一部分,沟通同样重要。几个我觉得能加分的做法:

主动说明你选择的存储结构以及为什么。比如"这个图比较稀疏,我用邻接表来存,空间是O(V+E)"。

在写代码前先用一个小例子手动走一遍思路,确认理解正确。这比闷头写代码然后发现方向错了要好得多。

写完代码后主动分析复杂度,并指出可能的优化方向。比如"这个BFS是O(V+E)的,已经是最优了"或者"如果边权非负,可以用Dijkstra替代Bellman-Ford来优化"。

遇到不确定的地方,主动和面试官确认。比如"题目说的图是有向还是无向?"、"节点编号是从0开始还是1开始?"这些确认不丢人,反而显得你严谨。

9. 从刷题到实战的思维转变

最后聊一个我观察到的现象:很多人刷了几百道图论题,面试遇到新题还是不会。根本原因是他们把每道题当成独立的题目来记,而不是把图算法当成一套工具来用。

正确的做法是建立一个"问题特征到算法"的映射。看到"最短路径"想到BFS或Dijkstra,看到"依赖关系"想到拓扑排序,看到"连通性"想到并查集或DFS,看到"环检测"想到拓扑排序或并查集。这个映射建立起来后,新题不过是旧工具的新组合。

另外,图论题目的一个特点是代码量比数组、字符串类题目大。面试中如果时间紧张,优先保证建图和核心逻辑正确,边界处理可以先用注释标出来,等核心逻辑跑通了再补。不要一上来就追求完美,结果核心逻辑还没写完时间就到了。

平时练习的时候,建议按算法类型分类刷,而不是按题目顺序刷。比如今天集中做5道BFS的题,明天集中做5道拓扑排序的题。这样更容易形成模式识别能力。每做完一道题,花两分钟想想"这道题的核心考点是什么""如果条件变一下该怎么改",比多做两道新题更有价值。

图这块内容确实多,但核心算法就那么几个。把DFS、BFS、拓扑排序、并查集、Dijkstra这五个吃透,面试中90%的图论题都能覆盖。剩下的时间可以放在理解变形和练习建模上,这比盲目刷题效率高得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 0:07:28

064、广播数据长度超出承载上限时的分包处理

现场&#xff1a;一条永远发不出去的广播 几年前做一个分布式采集系统&#xff0c;主控通过广播通道向所有节点下发同步命令和参数。协议栈的单帧净荷上限是 31 字节&#xff0c;这是链路层硬约束&#xff0c;改不了。前期功能很简单&#xff0c;同步命令就几个字节&#xff0c…

作者头像 李华
网站建设 2026/10/14 23:59:52

SpringBoot+Vue3社区养老服务平台全栈项目实战解析

1. 项目背景与整体技术选型近两年社区养老信息化一直是个热门的落地方向&#xff0c;特别是“人口老龄化社区服务与管理平台”这种系统&#xff0c;典型的SpringBoot 2 Vue 3组合&#xff0c;配合MyBatis-Plus和MySQL 8.0&#xff0c;基本就是Java Web全栈项目的标准答案。我前…

作者头像 李华
网站建设 2026/10/14 23:59:48

从核算到价值:财务转型五阶段路径与实操指南

做财务转型项目这些年&#xff0c;我最常听到的一句话是&#xff1a;“我们财务天天加班&#xff0c;老板还嫌我们不创造价值。”这句话听起来扎心&#xff0c;但确实点到了传统财务部的命门。核算型财务的典型状态是&#xff1a;月底结账、出报表、跑税务、审报销、核往来&…

作者头像 李华
网站建设 2026/10/14 23:59:28

零基础AI漫剧智能量产:从工具选型到批量出片的完整流程

1. 从零起步做AI漫剧&#xff1a;这个创作营到底在教什么第一次看到“零基础 AI 漫剧智能量产创作营”这个标题&#xff0c;我脑子里蹦出来的第一个念头是&#xff1a;又是一个割韭菜的课吧&#xff1f;但仔细拆解了一下“漫剧”“智能量产”“零基础”这三个关键词&#xff0c…

作者头像 李华
网站建设 2026/10/14 23:59:17

OCR文本行倾斜校正实战:基于OpenCV的霍夫变换与投影法解析

1. 倾斜校正的核心思路与方案选型做OCR或者文档结构化处理时&#xff0c;最让人头疼的往往不是识别模型本身&#xff0c;而是输入图像的质量。尤其是拍照或扫描的文档&#xff0c;文本行多多少少都有倾斜&#xff0c;有的歪个两三度&#xff0c;有的甚至歪到十几度&#xff0c;…

作者头像 李华
网站建设 2026/10/14 23:59:12

解决 pinia-plugin-persistedstate 在小程序中的 localStorage 报错

1. 问题现场还原&#xff1a;一个看似简单的报错&#xff0c;卡住了整个小程序先说结论&#xff1a;这个报错十有八九不是 pinia-plugin-persistedstate 本身写错了&#xff0c;而是小程序环境和浏览器环境的存储机制有差异导致的。说白了&#xff0c;这个插件默认是为浏览器里…

作者头像 李华