news 2026/10/15 4:55:47

链表算法刷题总忘?吃透指针操作与五类高频题型就够了

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
链表算法刷题总忘?吃透指针操作与五类高频题型就够了

1. 链表题刷了忘、忘了刷?问题多半出在"死记题解"上

很多人在链表这块的体验是这样的:今天看完反转链表的题解,觉得"就这?",明天碰到反转链表 II 又懵了;后天看到环形链表,记得要用快慢指针,但"为什么快指针走两步、慢指针走一步就一定能相遇"根本说不清楚。于是陷入循环——刷题、遗忘、再刷、再遗忘,最后怀疑自己的记忆力。

我在某平台把链表专项刷了三轮之后,一个很深的体会是:链表题根本不需要背,它需要的是"拆解"。链表这个数据结构一共就那么几种变化:指针怎么走、节点怎么换、边界怎么处理。把这些底层逻辑吃透,题目怎么包装都绕不开这几件事。这篇文章就是把我在刷题过程中整理出的链表核心框架、高频题型的拆解思路、以及那些容易让人栽跟头的边界细节完整记录下来,希望对正在刷链表专项的朋友有帮助。

链表题适合谁看?一类是刚开始刷 LeetCode、对指针操作还很生疏的新手,你可以把这篇当成一个带原理的索引;另一类是已经刷过一些题、但总在"会做和做对"之间反复横跳的人,这篇的重点就是帮你把错误模式归类,知道下次该往哪个方向检查。

2. 链表题的根本:指针操作只有三种动作

在拆具体题目之前,先把链表题最底层的东西说清楚。链表题不管怎么出,落到代码层面就是三个动作:改 next、移动指针、处理边界。你仔细观察每道题的解法和 Bug,几乎都能归类到这三件事里。

2.1 改 next:所有链表操作的原子动作

链表节点就是一堆"值 + 指向下一个节点的引用"。你做任何操作,本质都是在改某个节点的 next 指向谁。比如反转链表,就是把每个节点的 next 从"指向后一个"改成"指向前一个";删除节点,就是把前一个节点的 next 从"指向被删节点"改成"指向被删节点的下一个";合并两个有序链表,就是比较两个链表的头节点,把较小的那个的 next 指向"剩下链表合并后的头"。

这里容易忽略的一点是:改 next 是有顺序的。在链表中,如果你先把 A 的 next 改成 B,但 B 原本是 A 的下一个节点,那么 B 之后原来的节点就找不回来了——除非你提前用一个变量把它存下来。这也是很多人写链表题总出 Bug 的第一大原因:指针覆盖的顺序错了。

2.2 移动指针:循环里的推进步调

移动指针是链表题循环体的基本动作,常见的模式是 cur = cur.next。但不同题型里,指针移动的步调和数量完全不一样,这也是需要专门理解的部分。

  • 遍历链表:一个指针,每次走一步,到 null 停下。
  • 找中点:快慢指针,快指针每次走两步,慢指针每次走一步,快指针到末尾时慢指针就在中点。
  • 找倒数第 N 个节点:两个指针,第一个先走 N 步,然后两个一起走,第一个走到 null 时第二个就在目标位置。
  • 判断环:快慢指针,同上,但需要额外判断相遇条件。

指针移动的问题还涉及"循环终止条件"。很多人写 while 循环时只记得 p != null,但在需要用到 p.next 的场景里,得改成 p != null && p.next != null,否则循环体里访问 p.next.next 就会空指针。这种错误特别典型,几乎每个链表题的新手都栽过。

2.3 边界处理:空链表、单节点、头节点变更

链表题有一个特点:代码主体的逻辑并不难,真正决定你能不能一次通过的,是边界条件。最典型的就是这三类:

  1. 空链表:head 为 null 时,所有操作都应该直接返回 null 或不执行。
  2. 单节点:只有一个节点时,反转、删除、找中点等操作是否正确。
  3. 头节点被改变:删除头节点、反转后头节点变成原尾节点,这时要知道怎么返回新的头。

这些边界问题用通用手段就能解决,其中最常用的就是虚拟头节点(dummy node)。给链表强行加一个"前置空节点",让头节点的处理和中间节点完全一致,很多分类讨论直接消失。这个技巧在链表题里出现的频率极高,后面每个高频题型里都会用到。

3. 专项拆解:五类高频链表题的通用解法框架

链表题刷到一定量之后,你会发现高频题型其实是有限的。LeetCode 链表篇的核心题,大致可以归纳成五大类:反转类、合并类、删除类、环与相交类、以及综合重排类。下面按类别拆它们的核心套路。

3.1 反转类:从"迭代三指针"到"递归理解"

反转链表是链表题的基础款,也是后续好多题的地基。比如反转链表 II、K 个一组翻转链表、回文链表等,全都依赖反转操作。

迭代法的核心是三个指针:prev、cur、next。

  • prev 初始为 null,表示"当前节点的前一个节点"。
  • cur 初始为 head,表示"当前要处理反转的节点"。
  • 每轮循环先存 next = cur.next(防止断链),然后把 cur.next 指向 prev,接着 prev 移动到 cur,cur 移动到 next。

这个过程中最关键的操作就是"先存 next"。我在前面提到过"改 next 有顺序",反转链表就是最经典的例子:如果不先把 cur.next 存下来,cur.next 被改成 prev 之后,原来的后续节点就全丢了。

递归法的思路不太一样,它的核心是"把当前节点的后继节点的 next 指向当前节点"。递归函数 reverse(head) 的返回值是反转后的新头。对递归版本,我的建议是:先把它当成"黑盒"理解,即假设 reverse(head.next) 已经帮我们把后面那段反转好了,我们只需要处理 head 和 head.next 的关系。等用熟了,再去深入理解递归栈的展开过程。

实测建议:面试或笔试场景优先迭代法,因为好写、好调试、空间复杂度是 O(1)。递归法虽然代码短,但递归深度受链表长度影响,链表特别长时(比如上万节点)容易出现栈溢出,而且递归版本的空间复杂度是 O(n)。

3.2 合并类:两种合并,一个套路

合并类题目主要包括"合并两个有序链表"和"合并 K 个有序链表"。前者是基础,后者是进阶。

合并两个有序链表的思路非常直观:用两个指针分别指向两个链表头部,比较值的大小,把较小者接到结果链表上,然后移动对应指针。这里有一个常用技巧——虚拟头节点 + 尾指针。

  • 虚拟头节点 dummy,作为结果链表的头之前的占位节点。
  • 尾指针 tail 一开始指向 dummy,每次把选中的节点接到 tail.next,然后 tail 向后移动。
  • 循环直到其中一个链表为空,然后把另一个链表的剩余部分直接接上。

直接接上剩余部分这个操作,很多人会写成循环逐个接。其实链表可以整体接,因为剩余部分本身就已经是有序链表了,尾指针的 next 直接指向剩余链表的头节点即可,这是链表操作里非常实用的小优化。

合并 K 个有序链表的朴素做法是每两个合并一次,但那样效率低。面试中更推荐用优先队列(小顶堆)来做:把所有链表的头节点放进堆,每次弹出最小的节点接入结果链表,同时把该节点的下一个节点入堆。这样每个节点只会入堆出堆各一次,时间复杂度是 O(n log k),其中 n 是总节点数,k 是链表数量。

3.3 删除类:虚拟头节点是低配玩家的神

删除类题目的典型代表是"删除链表的倒数第 N 个节点",以及"删除排序链表中的重复元素"系列。

删除倒数第 N 个节点的核心是"找到要删节点的前一个节点"。因为单链表只能从前往后走,删除操作必须操作前驱节点。最常用的方法就是双指针,或者叫"延迟指针"思路:

  1. 第一个指针先走 N 步。
  2. 然后第二个指针从 head 出发,两个指针同步走,第一个指针走到 null 时,第二个指针正好停在倒数第 N 个节点的前一个位置。
  3. 执行删除,即 prev.next = prev.next.next。

最容易出 Bug 的点:如果要删的就是头节点怎么办?比如链表只有 5 个节点,要删倒数第 5 个(也就是头节点)。这时如果按上面的双指针来走,第二个指针停在 head 上,没法通过"前驱节点的 next"来删除。解决方法仍然是虚拟头节点——让两个指针都从 dummy 出发,第二个指针最终会停在 dummy(也就是头节点的前驱),删除操作就统一了。

删除排序链表重复元素有两个版本。一个是"只保留一个",用 cur 指针遍历,如果 cur.next 的值和 cur 相同,就跳过 cur.next;另一个是"重复的全删",这种需要额外注意:如果头节点本身是重复的,头节点也要删,所以更推荐用 dummy 统一处理。

3.4 环与相交类:快慢指针的数学原理

环形链表和相交链表这两类题,表面上看场景完全不同,但底层的双指针思想是相通的。

判断链表是否有环:快慢指针的经典应用。slow 每次走一步,fast 每次走两步,如果链表有环,两者一定会在环内相遇。为什么?可以这样理解:当 slow 进入环时,fast 已经在环里了。假设环的长度为 L,此时 fast 离 slow 的距离是 d(0 < d ≤ L),每走一轮,fast 相对 slow 的位移是 1,所以最多 L 轮之后 fast 一定会追上 slow。这就是"快指针走两步"的数学依据。有人问为什么不走三步?走三步也能追上,但不是每轮追一步,可能跨过 slow 而造成"跳过"的情况,还需要处理步长和环长的整除问题,实现更复杂。

找到环的入口:在快慢指针相遇后,把其中一个指针移到 head,然后两个指针都以步长 1 前进,再次相遇的位置就是环入口。这个结论看着像魔术,其实可以推导:假设 head 到环入口的距离为 a,环入口到相遇点的距离为 b,环长度为 L。相遇时 slow 走了 a + b,fast 走了 a + b + kL(k 是 fast 在环里多绕的圈数),又因为 fast 走的路程是 slow 的两倍,所以 2(a + b) = a + b + kL,推出 a = kL - b。也就是说,head 到入口的距离,等于相遇点继续走到入口的距离。所以让一个指针从 head 出发、一个从相遇点出发,同步走,一定会在入口相遇。

相交链表的常规解法是先分别求两个链表的长度,让长链表先走步数差,然后两个指针同步走,第一个相等节点就是交点。更简洁的做法是"双指针互相走完对方的链表":pA 从 A 头出发,走到末尾后跳到 B 头;pB 从 B 头出发,走到末尾后跳到 A 头。如果两个链表相交,它们会在交点相遇;如果不相交,会在最后同时到达 null。这个解法代码短,但第一次看的时候很难想明白,建议配合链表长度差的思路去理解。

3.5 重排类:三种操作的组合拳

重排类题目比如"重排链表"(L0 → Ln → L1 → Ln-1 → ...)和"回文链表",通常可以拆成三步组合:

  1. 找中点:用快慢指针找到链表的中点,或者中点前一个节点。
  2. 反转后半段:将中点之后的链表反转。
  3. 合并/比较两段:把前半段和后半段进行合并,或者逐节点比较值。

以"重排链表"为例:

  • 找中点,将链表切成前后两半。
  • 把后半段反转,得到新链表。
  • 将两个链表交替合并:先取前半段第一个节点,再接后半段第一个节点,再取前半段第二个节点……

这类题的价值在于,它把前面对反转、双指针、遍历的理解全串起来了,属于综合题。如果你前面几类都掌握得还行,但重排类老是不能一遍写对,大概率是卡在"找中点的边界"或者"链表中点后面那段的起点确认"上。

具体来说,找中点有两种写法:一种是让 fast 从 head 出发,走两步,slow 走一步,循环结束后 slow 指向中间节点;另一种是让 fast 从 head.next 出发,循环结束后 slow 指向前半段的最后一个节点。两种写法在"节点个数是偶数"时,slow 的落点不同。刷题时要明确自己用的是哪种,不然下一步反转后半段时会把中间节点搞重复。

4. 刷题实战中的血泪教训:必须避开的五个坑

链表题的代码一般不长,20 到 40 行就能写完,但 LeetCode 的提交记录里,链表题的提交错误率一直不低。我自己刷完三轮后复盘,发现错误主要集中在下面五个坑里,而且这些坑是有规律地反复出现的。

4.1 空指针访问:p.next 之前必须确认 p 不为 null

这是链表题第一大类错误。典型场景是在循环里直接写 cur.next.next,却没有保证 cur 和 cur.next 都不为 null。

我踩过的一个非常具体的场景是"删除链表的倒数第 N 个节点"的双指针写法里,第一个指针先走 N 步时,如果 N 正好等于链表长度,那么第一个指针刚好移到 null。接着第二个指针开始同步走,循环条件是第一个指针不为 null,这个没问题;但如果在循环体内访问第一个指针的 next 做判断,就会空指针。

经验法则:凡是代码里出现 p.next,你就要问自己:p 有没有可能是 null?凡是出现 p.next.next,你就要问自己:p.next 有没有可能是 null?做题时先把所有边界情况列出来,再动手写。

4.2 断链:被覆盖的 next 没有提前保存

链表操作中等价于"拆链再挂链"。拆链之前,被拆的那个节点的 next 可能指向后续节点,如果你把它覆盖成别的值,又没保存原 next,后续节点就丢了。

反转链表是最典型的情况。很多人在写反转循环时,习惯先把 cur.next 改了,然后再去找原来的下一个节点,结果发现找不到了。正确顺序永远是:先用临时变量保存 next,再改 cur.next,最后移动指针。

这个"先保存再修改"的习惯,在几乎所有会修改节点间关系的题目里都通用,包括删除节点、交换相邻节点、重排链表、反转链表 II 等。

4.3 循环边界:while 条件里 p 与 p.next 的搭配

链表题的循环条件有时候是 p != null,有时候是 p.next != null,选错会直接导致漏处理尾节点或者多处理一个空节点。

我自己的使用习惯是:

  • 如果循环体里需要"当前节点"和"下一个节点"的比较(比如删除重复元素、合并有序链表),通常用 cur != null && cur.next != null。
  • 如果循环体只处理当前节点本身,比如反转链表,用 cur != null 即可。
  • 找中点时,让快指针走到尽头,循环条件一般是 fast != null && fast.next != null。

一个比较通用的做法是:写循环之前先想清楚退出循环时,指针停在哪里。这个"停点"决定了你接下来要处理的节点是哪一个,也决定了边界判断怎么写。

4.4 中间节点归属:偶数长度链表的"中点模糊"

找中点在很多题里是第一步,但中点的定义在链表中有歧义:偶数长度的链表,中点到底偏左还是偏右?

比如链表 1 -> 2 -> 3 -> 4,中点可以是 2,也可以是 3。两种定义对应的代码写法不一样,后续操作也不一样。重排链表一般希望把链表分成两半,前半段是 1 -> 2,后半段是 3 -> 4,那么中点取 2(前半段末尾)更合适。而回文链表比较前后两半时,把中点取成 2 或 3 都能工作,只要后面比较的范围写对。

我的建议:在练习每道题时,主动用偶数长度链表面试自己——如果把链表的长度改成偶数,我的代码还能跑对吗?不能的话,就是中点归属的处理有问题。

4.5 头节点可变:忘记更新返回值

这个问题在新手里太常见了。写完反转链表,函数最后返回的还是 head,但此时 head 已经变成末尾节点了。链表的头变成了原来的尾节点,如果返回 head,整个链表相当于只剩一个节点,或者直接形成环。

养成一个习惯:如果一个操作可能让头节点发生变化,先明确新的头节点是谁。反转链表的新头是原链表的尾节点;删除头节点后的新头是原第二个节点;交换相邻节点时新头是原第二个节点。要么单独用一个变量保存新头,要么用虚拟头节点让头节点相对位置不变,返回 dummy.next。

5. 实操记录:一道综合题的完整推导过程

光讲框架不给案例,等于没说。我用一道中等难度的综合题来完整走一遍推导过程,题目是"两两交换链表中的相邻节点"(LeetCode 24)。

这个题的描述很简单:给定链表 1 -> 2 -> 3 -> 4,返回 2 -> 1 -> 4 -> 3。看起来很容易,但实际写的时候,指针步调只要乱一步就出错。

5.1 第一步:确定要操作的节点

两两交换,意味着每轮要处理两个节点。以第一轮为例,涉及 1、2 两个节点,而它们的上一个节点是 dummy。我们需要的指针有:

  • prev:当前待交换的一对节点的前一个节点。
  • first:第一个待交换节点。
  • second:第二个待交换节点。

如果用 prev.next 表示 first,prev.next.next 表示 second,那么两个节点交换后,顺序变为 second -> first,而 second 原本的 next 要接回原链表剩余部分。

5.2 第二步:写出每一轮的指针变动序列

我来写具体代码,并标注每一步的意义:

var swapPairs = function(head) { let dummy = new ListNode(0, head); let prev = dummy; while (prev.next !== null && prev.next.next !== null) { let first = prev.next; let second = prev.next.next; // 第一步:first 指向 second 的下一个节点 first.next = second.next; // 第二步:second 指向 first second.next = first; // 第三步:prev 指向 second prev.next = second; // 第四步:移动 prev 到 first,准备处理下一对 prev = first; } return dummy.next; };

这里有一个非常关键的细节:四步操作的顺序不能变。

如果你先把 second.next 改成 first,那么 second 原本的下一个节点(第三对及以后)就找不到了,因为 first.next 还没被更新,而且你也没有提前保存 second.next。正确顺序是先让 first 挂到 second 的后面(也就是剩余链表),再让 second 指向 first,这样原链表剩余部分不会丢。

5.3 第三步:边界检查

写完代码,别急着提交。先用三个边界测一下:

  • 空链表:head 为 null,dummy.next 为 null,返回 null,正确。
  • 单节点:循环条件不满足(prev.next.next 为 null),直接返回 dummy.next,仍然是原链表,正确。
  • 奇数长度链表:比如 1 -> 2 -> 3,循环走完第一轮后 prev 移到 1,此时 prev.next 是 3,prev.next.next 是 null,循环终止,3 保持不动,正确。

这道题如果不用 dummy,处理头节点交换时会很麻烦,因为头节点的前驱不存在。而用了 dummy,整个过程就和中间节点的处理完全统一了。

5.4 第四步:从这道题里提炼通用技巧

这道题最有价值的点在于,它把链表操作里最常见的三个注意事项全部体现了一遍:先保存后修改、操作前驱节点、用 dummy 统一边界。

做完这道题,你再回头去看"反转链表 II"、"K 个一组翻转链表",会发现它们的代码结构非常相似——都有一个 prev 指向待处理区间前的节点,都在循环里先保存 next,再改指向,再移动 prev。这些题的核心框架其实是一样的,只是每次要反转的节点数量从 2 变成 k,边界判断更复杂了一点。

6. 链表专项刷题路线与效率工具

如果你准备系统地刷链表专项,我建议按下面的顺序推进,这条路线的设计逻辑是:先用基础题建立"改指针"的感觉,再用中等题强化边界处理,最后用综合题把各个技能串起来。

6.1 推荐刷题顺序

第一阶段:基础操作题。刷"反转链表"、"合并两个有序链表"、"删除链表的倒数第 N 个节点"。这三道题分别对应反转、合并、删除三个基础操作,而且代码量都不大,适合建立手感。

第二阶段:结构变换题。刷"两两交换链表中的节点"、"反转链表 II"、"K 个一组翻转链表"。这三道题逐渐增加区间处理的复杂度,需要你对 prev、cur、next 的移动非常敏感。

第三阶段:双指针技巧题。刷"环形链表"、"环形链表 II"、"相交链表"、"删除排序链表中的重复元素 II"。这类题的重点是指针的数学关系和边界分类。

第四阶段:综合题。刷"重排链表"、"回文链表"、"排序链表"。这三道题都需要你把前面的技巧组合使用,能独立写对这三道,链表专项基本就算是过关了。

6.2 用什么语言刷

语言选择上,我的建议是:如果是练习阶段,用你面试或工作中最常用的语言。链表操作在 C++ 里涉及指针,在 Java/JavaScript 里涉及引用,在 Python 里涉及对象引用,语法细节不同,但核心逻辑完全一致。我在本地练习时用 JavaScript 多一些,因为写起来快,不需要考虑内存管理,适合快速验证思路;但如果你要准备面试,建议用目标公司的面试语言练熟,因为"链表的指针操作"在很多面试里会要求手写,语言不熟会很吃亏。

6.3 做题时的笔记方法

我刷链表题时养成了一个习惯:每道题提交通过之后,用两句话总结"这道题的核心操作"和"这道题的边界陷阱",记在表格里。比如:

题目类型核心操作最常见 Bug
反转链表三指针迭代忘记先保存 next
删除倒数第 N 个节点双指针延迟 + dummy头节点被删时前驱缺失
环形链表快慢指针循环条件写成 fast.next 而不判 fast
重排链表找中点 + 反转后半段 + 交替合并偶数长度中点偏左偏右混淆

这个表格不需要很复杂,但它能帮你快速定位薄弱点。等到第二轮刷题时,你可以只看这个表,回忆每道题的完整解法,回忆不出来的再回去看代码。

7. 写在最后:链表题到底该怎么练?

链表题刷到后面,我最大的感受是:它其实是最接近"物理操作"的一类算法题。树的题有递归和遍历框架可以套,动态规划的题有状态定义和转移方程可以依赖,而链表题就是纯粹的指针游戏——你把节点想成真实的盒子,每个盒子上有一根绳子指向下一个盒子。你的任务就是不断拆绳子、绑绳子、换绳子,而且要保证过程中没有任何一个盒子被"弄丢"。

所以练习链表题,最好的方式真的就是在纸上画。每道题,先把链表画出来,标上指针,然后手动走一遍循环,看看每一步指针怎么变。很多在代码里看不出来的问题,一画就清楚了。我个人刷完三轮之后,几乎每道链表题都能做到不看题解写出来,靠的就是这个"画图 + 手动模拟"的习惯。

如果你现在正处于"链表题总是差一点"的状态,别急着加题量。找一道你做过的题,用画图的方式重新走一遍整个流程,把边界情况都列出来,多半会有一种"原来如此"的感觉。这个数据结构就是纸老虎,捅破那层窗户纸之后,你会发现 LeetCode 链表篇的题目,翻来覆去就是那么回事。

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

Flink SQL 实时开发实战:从 DataStream 到声明式 SQL 的完整链路

1. 为什么 Flink SQL 成了实时开发者的“真香”选择刚接触实时计算那会儿&#xff0c;我第一反应是去写 DataStream API&#xff0c;Java 代码一坨一坨地堆&#xff0c;窗口、状态、水位线全得自己管。后来团队接了个实时看板的需求&#xff0c;指标口径一天变三回&#xff0c;…

作者头像 李华
网站建设 2026/10/15 4:55:09

蒙特卡洛摸球实验

2026.10.141.定义蒙特卡洛方法是指用大量随机试验&#xff0c;靠频率估算概率 面积 数值&#xff0c;属于随机模拟方法2.优缺点优点&#xff1a;原理简单&#xff0c;编程实现方便&#xff1b;能够处理解析方法难以求解的复杂、高维问题&#xff0c;适用范围广。 缺点&#xff…

作者头像 李华
网站建设 2026/10/15 4:55:02

Linux日志排查七步法:从tail/grep到trace_id关联的实战指南

1. 项目概述&#xff1a;为什么“查日志”不是翻文件&#xff0c;而是一门现场诊断术“一些查日志的技巧记录”——这个标题看着平淡&#xff0c;甚至有点像随手记在笔记本角落的备忘&#xff0c;但在我带过的十几个运维、开发、测试和SRE团队里&#xff0c;它恰恰是新人转正前…

作者头像 李华
网站建设 2026/10/15 4:55:02

自建AI情绪日记小程序:需求、设计、AI接入与复盘实践

前言多说无益&#xff0c;我直接讲一件让我决定动手的事。市面上的情绪日记应用&#xff0c;我前后换了七八个&#xff0c;没有一个能让我连续记满两个月。有的模板太重&#xff0c;每次打开要回答一长串问题&#xff1b;有的又太轻&#xff0c;只让选个表情&#xff0c;写不了…

作者头像 李华
网站建设 2026/10/15 4:54:37

WAF跨域配置排障指南:四步定位头字段丢失与预检失败

1. 这不是“加个header”就能解决的配置问题很多人第一次接触“WAF跨域配置”&#xff0c;第一反应是&#xff1a;“不就是往响应头里加个Access-Control-Allow-Origin: *吗&#xff1f;配完刷新页面&#xff0c;报错还在——甚至更糟。”我见过太多开发在联调阶段卡在这一步&a…

作者头像 李华
网站建设 2026/10/15 4:54:25

Java WebSocket聊天室实战:从零搭建高并发实时通信系统

1. 项目概述&#xff1a;为什么一个“老技术”还能撑起整套实时交互系统&#xff1f;WebSocket不是新概念&#xff0c;但直到今天&#xff0c;它依然是构建低延迟、高并发聊天室最稳当的底层选择。我带过不少刚学完Spring Boot的新手做毕业设计&#xff0c;一上来就想用WebRTC或…

作者头像 李华