⭐️ 本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 和 [BaguTree Pro] 知识星球提问。
学习数据结构与算法的关键在于掌握问题背后的算法思维框架,你的思考越抽象,它能覆盖的问题域就越广,理解难度也更复杂。在这个专栏里,小彭将基于 Java / Kotlin 语言,为你分享常见的数据结构与算法问题,及其解题框架思路。
本文是数据结构与算法系列的第 1 篇文章,完整文章目录请移步到文章末尾~
前言
- 链表的相关问题,在面试中出现频率较高,这些问题往往也是解决其他复杂问题的基础;
- 在这篇文章里,我将梳理链表问题的问题 & 解法。如果能帮上忙,请务必点赞加关注,这真的对我非常重要。
删除链表节点 |
提示 & 题解 |
---|---|
203. 移除链表元素 Remove Linked List Elements | 【题解】 |
237. 删除链表中的节点 Delete Node in a Linked List | 【题解】 |
19. 删除链表的倒数第N个节点 Remove Nth Node From End of List | 【题解】 |
86. 分隔链表 Partition List | 【题解】 |
328. 奇偶链表 Odd Even Linked List | 【题解】 |
83. 删除排序链表中的重复元素 Remove Duplicates from Sorted List | |
82. 删除排序链表中的重复元素 II Remove Duplicates from Sorted List II | |
725. 分隔链表 Split Linked List in Parts | |
(扩展)0876. 链表的中间结点 Middle of the Linked List | 【题解】 |
反转链表 |
提示 & 题解 |
---|---|
206. 反转链表 Reverse Linked List | 【题解】 |
92. 反转链表 II Reverse Linked List II | 【题解】 |
234. 回文链表 Palindrome Linked List | 【题解】 |
25. K 个一组翻转链表 Reverse Nodes in k-Group |
合并有序链表 |
提示 & 题解 |
---|---|
21. 合并两个有序链表 Merge Two Sorted Lists | 【题解】 |
23. 合并K个升序链表 Merge k Sorted Lists | 【题解】 |
排序链表 |
提示 & 题解 |
---|---|
147. 对链表进行插入排序 Insertion Sort List | 【题解】 |
148. 排序链表 Sort List | 【题解】 |
环形链表 |
提示 & 题解 |
---|---|
160. 相交链表 Intersection of Two Linked Lists | 【题解】 |
141. 环形链表 Linked List Cycle | 【题解】 |
142. 环形链表 II Linked List Cycle II | 【题解】 |
61. 旋转链表 Rotate List | 【题解】 |
其他 |
提示 & 题解 |
---|---|
24. 两两交换链表中的节点 Swap Nodes in Pairs | |
143. 重排链表 Reorder List | |
138. 复制带随机指针的链表 Copy List with Random Pointer | |
380. 常数时间插入、删除和获取随机元素 Insert Delete GetRandom O(1) | |
381. O(1) 时间插入、删除和获取随机元素 - 允许重复 Insert Delete GetRandom O(1) - Duplicates allowed | |
707. 设计链表 Design Linked List | |
430. 扁平化多级双向链表 Flatten a Multilevel Doubly Linked List | |
817. 链表组件 Linked List Components | 【题解】 |
目录
- 概述
1.1 链表的定义
链表是一种常见的基础数据结构,是一种线性表。与顺序表不同的是,链表中的每个节点不是顺序存储的,而是通过节点的指针域指向到下一个节点。
1.2 链表的优缺点
对比 | 优点 | 缺点 |
---|---|---|
内存管理 | 充分利用计算机内存空间,更灵活地分配内存空间 | 指针域增加了内存消耗 |
操作效率 | 能在 O(1)O(1)O(1) 时间内删除或添加节点(前提是前驱节点已知) | 失去了数组随机访问的特性,查询对应位置的节点需要 O(n)O(n)O(n) 时间 |
数据容量 | 需要预先分配内存空间,容量不足需要扩容 | 不需要预先分配内存空间,不需要扩容 |
访问性能 | / | CPU 缓存行无法提高效率 |
1.3 链表的类型
单链表、双链表、循环链表、静态链表
- 删除链表节点
删除链表节点时,考虑到可能删除的是链表的第一个节点(没有前驱节点),为了编码方便,可以考虑增加一个 哨兵节点。其中,在删除链表的倒数第 N 个节点问题里,使用快慢指针在一趟扫描里找出倒数第 N 个节点是比较重要的编程技巧。
237. Delete Node in a Linked List 删除链表中的节点 【题解】
203. Remove Linked List Elements 移除链表元素 【题解】
1 | kotlin复制代码不移除野指针 |
19. Remove Nth Node From End of List 删除链表的倒数第N个节点 【题解】
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
1 | kotlin复制代码class Solution { |
复杂度分析:
- 时间复杂度:每个节点扫描一次,时间复杂度为 O(n)O(n)O(n)
- 空间复杂度:使用了常量级别变量,空间复杂度为 O(1)O(1)O(1)
类似地,876. Middle of the Linked List 链表的中间结点 【题解】 也是通过快慢指针来找到中间节点的:
1 | kotlin复制代码class Solution { |
86. Partition List 分隔链表 【题解】
删除链表中等于给定值 val 的所有节点。
思路:分隔链表无非是先将大于等于 val 的节点从原链表中移除到第二个链表中,最后再拼接两个链表。
1 | kotlin复制代码class Solution { |
复杂度分析:
- 时间复杂度:每个节点扫描一次,时间复杂度为 O(n)O(n)O(n)
- 空间复杂度:使用了常量级别变量,空间复杂度为 O(1)O(1)O(1)
328. Odd Even Linked List 奇偶链表 【题解】
思路:奇偶链表无非是先将奇节点放在一个链表里,偶节点放在另一个链表里,最后把偶节点接在奇链表的尾部
1 | kotlin复制代码class Solution { |
83. Remove Duplicates from Sorted List 删除排序链表中的重复元素
82. Remove Duplicates from Sorted List II 删除排序链表中的重复元素 II
- 反转链表
反转链表问题在面试中出现频率 非常非常高,相信有过几次面试经验的同学都会同意这个观点。在这里,我找出了 4 道反转链表的问题,从简单延伸到困难,快来试试吧。
206. 反转链表 Reverse Linked List 【题解】
反转一个单链表。
解法1:递归
1 | kotlin复制代码class Solution { |
复杂度分析:
- 时间复杂度:每个节点扫描一次,时间复杂度为 O(n)O(n)O(n)
- 空间复杂度:使用了递归栈,空间复杂度为 O(n)O(n)O(n)
解法2:迭代
1 | kotlin复制代码class Solution { |
复杂度分析:
- 时间复杂度:每个节点扫描一次,时间复杂度为 O(n)O(n)O(n)
- 空间复杂度:使用了常量级别变量,空间复杂度为 O(1)O(1)O(1)
92. 反转链表 II Reverse Linked List II 【题解】
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
1 | kotlin复制代码class Solution { |
复杂度分析:
- 时间复杂度:每个节点扫描一次,时间复杂度为 O(n)O(n)O(n)
- 空间复杂度:使用了常量级别变量,空间复杂度为 O(1)O(1)O(1)
234. Palindrome Linked List 回文链表 【题解】
请判断一个链表是否为回文链表。
思路:使用快慢指针找到中间节点,反转后半段链表(基于反转链表 II),比较前后两段链表是否相同,最后再反转回复到原链表。
1 | kotlin复制代码class Solution { |
复杂度分析:
- 时间复杂度:每个节点扫描两次,时间复杂度为 O(n)O(n)O(n)
- 空间复杂度:使用了常量级别变量,空间复杂度为 O(1)O(1)O(1)
25. K 个一组翻转链表 Reverse Nodes in k-Group
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
- 合并有序链表
合并有序链表问题在面试中出现频率 较高,其中,合并两个有序链表 是比较简单的,而它的进阶版 合并K个升序链表 要考虑的因素更全面,难度也有所增强,快来试试吧。
21. Merge Two Sorted Lists 合并两个有序链表 【题解】
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
1 | kotlin复制代码class Solution { |
复杂度分析:
- 时间复杂度:每个节点扫描一次,时间复杂度为 O(m+n)O(m + n)O(m+n)
- 空间复杂度:使用了常量级别变量,空间复杂度为 O(1)O(1)O(1)
23. Merge k Sorted Lists 合并K个升序链表 【题解】
给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。
解法1:暴力法
思路1:与合并两个有序链表类似,每轮从 k 个链表中取出最小的节点,并插入结果链表中。其中,从 k 个数中取出最小节点的时间复杂度为 O(k)O(k)O(k)。
思路2:这个思路与上个思路类似,时间复杂度和空间复杂度页相同,即:依次将 k 个链表与结果链表合并。
1 | 复制代码略 |
复杂度分析:
- 时间复杂度:O(nk∗k)O(nk * k)O(nk∗k)
- 空间复杂度:O(1)O(1)O(1)
解法2:排序法
思路:用一个数组保存所有节点之后,进行快速排序,随后将数组输出单链表。
1 | kotlin复制代码class Solution { |
复杂度分析:
- 时间复杂度:合并节点时间 O(nk)O(nk)O(nk),快速排序时间 O(nk∗lgnk)O(nk*lgnk)O(nk∗lgnk),输出单链表时间 O(nk)O(nk)O(nk),总体时间复杂度 O(nk∗lgnk)O(nk*lgnk)O(nk∗lgnk)
- 空间复杂度:使用数组空间 O(nk)O(nk)O(nk)
解法3:归并法
思路:将 k 组链表分为两部分,然后递归地处理两组链表,最后再合并起来。
1 | kotlin复制代码class Solution { |
复杂度分析:
- 时间复杂度:时间主要在合并链表的操作上,从递归树可以看出,递归树每一层的节点个数都是 nknknk,而递归树的高度 h=lgkh = lgkh=lgk,因此总的时间复杂度为 O(nk∗lgk)O(nk*lgk)O(nk∗lgk)
- 空间复杂度:使用了递归栈,空间复杂度为 O(lgk)O(lgk)O(lgk)
解法4:小顶堆法
思路:在解法1中,从 k 个数中取出最小节点的时间复杂度为 O(k)O(k)O(k),可以使用最小堆(优先队列)来优化到 O(lgk)O(lgk)O(lgk)。其中,堆内节点始终是 k 个链表的未处理部分的表头。
1 | kotlin复制代码class Solution { |
复杂度分析:
- 时间复杂度:大小为 k 的二叉堆建堆时间为 O(k)O(k)O(k),取堆顶的时间为 O(1)O(1)O(1),插入一个新节点的时间为 O(lgk)O(lgk)O(lgk),总体时间复杂度为 O(nk∗lgk)O(nk∗lgk)O(nk∗lgk)
- 空间复杂度:二叉堆空间为 O(k)O(k)O(k)
- 排序链表
147. Insertion Sort List 对链表进行插入排序 |【题解】
148. Sort List 排序链表 【题解】
- 环形链表
链表相交 & 成环问题可以归为一类问题,在面试中出现频率较高;在之前的一篇文章里,我们单独讨论过:数据结构与算法 #2 链表相交 & 成环问题总结
推荐阅读
数据结构与算法系列完整目录如下(2023/07/11 更新):
- #1 链表问题总结
- #2 链表相交 & 成环问题总结
- #3 计算器与逆波兰表达式总结
- #4 高楼丢鸡蛋问题总结
- #5 为什么你学不会递归?谈谈我的经验
- #6 回溯算法解题框架总结
- #7 下次面试遇到二分查找,别再写错了
- #8 什么是二叉树?
- #9 什么是二叉堆 & Top K 问题
- #10 使用前缀和数组解决 “区间和查询” 问题
- #11 面试遇到线段树,已经这么卷了吗?
- #12 使用单调队列解决 “滑动窗口最大值” 问题
- #13 使用单调栈解决 “下一个更大元素” 问题
- #14 使用并查集解决 “朋友圈” 问题
- #15 如何实现一个优秀的 HashTable 散列表
- #16 简答一波 HashMap 常见面试题
- #17 二叉树高频题型汇总
- #18 下跳棋,极富想象力的同向双指针模拟
- #19 ChatGPT 通过谷歌算法面试,年薪 18.3 万美金
- #20 不难但极其经典的搜索模拟
Java & Android 集合框架系列文章: 跳转阅读
LeetCode 上分之旅系列文章:跳转阅读
⭐️ 永远相信美好的事情即将发生,欢迎加入小彭的 Android 交流社群~
本文转载自: 掘金