1. 项目概述
今天要分享的是代码随想录算法训练营第八天的两道经典题目:LeetCode 344反转字符串和LeetCode 542反转链表II。这两道题在面试中出现频率极高,也是理解指针操作的基础。作为算法训练营的第八天内容,它们很好地衔接了前面学习的数组和链表知识,同时为后续更复杂的算法打下基础。
反转字符串看似简单,但能考察对双指针法的掌握程度;而反转链表II则需要处理更复杂的指针关系,特别是当只反转链表中间某一部分时,边界条件的处理尤为关键。这两道题我都反复练习过多次,也总结出了一些实用的调试技巧和易错点,今天会毫无保留地分享给大家。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心思路解析
2.1 LeetCode 344 反转字符串
这道题的要求很简单:编写一个函数,将输入的字符串反转过来。输入字符串以字符数组的形式给出,必须原地修改输入数组,使用O(1)的额外空间。
双指针法是最直观的解法:
- 初始化两个指针,left指向数组头部(索引0),right指向数组尾部(索引len(s)-1)
- 当left < right时:
- 交换s[left]和s[right]的值
- left向右移动一位,right向左移动一位
- 重复直到left >= right
这个方法的优势在于:
- 时间复杂度O(n),只需遍历一半数组
- 空间复杂度O(1),只使用了常数个额外变量
- 适用于任何编程语言,思路通用
注意:很多人在处理边界条件时容易犯错,特别是当字符串长度为奇数时,中间元素不需要处理,这个细节在面试中经常被考察。
2.2 LeetCode 542 反转链表II
这道题比简单的反转整个链表要复杂一些:要求反转从位置m到n的链表节点。必须一趟扫描完成反转。
解题步骤:
- 创建dummy节点指向head,处理m=1的情况
- 找到第m-1个节点(pre)和第n+1个节点(succ)
- 切断子链表,反转m到n的部分
- 将反转后的子链表重新接入原链表
关键点在于:
- 使用dummy节点统一处理头节点可能被反转的情况
- 反转前需要记录pre和succ节点位置
- 反转后需要正确连接三部分链表
3. 代码实现与详细注释
3.1 反转字符串实现
python复制def reverseString(s: List[str]) -> None:
"""
反转字符串
:param s: 字符列表
:return: 无返回值,原地修改
"""
left, right = 0, len(s) - 1
while left < right:
# 交换左右指针位置的字符
s[left], s[right] = s[right], s[left]
# 移动指针
left += 1
right -= 1
调试技巧:
- 可以在循环中加入打印语句,观察每次交换后的字符串状态
- 对于空字符串或单字符字符串,代码也能正确处理
- Python中可以直接用s[::-1],但面试时通常要求实现底层逻辑
3.2 反转链表II实现
python复制class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def reverseBetween(head: ListNode, m: int, n: int) -> ListNode:
if not head or m == n:
return head
# 创建dummy节点处理m=1的情况
dummy = ListNode(0)
dummy.next = head
pre = dummy
# 找到第m-1个节点
for _ in range(m - 1):
pre = pre.next
# 初始化反转所需的指针
current = pre.next
prev = None
# 反转m到n的节点
for _ in range(n - m + 1):
next_node = current.next
current.next = prev
prev = current
current = next_node
# 连接反转后的子链表
pre.next.next = current
pre.next = prev
return dummy.next
关键点解析:
- dummy节点的使用避免了单独处理头节点被反转的情况
- 反转前需要先找到pre节点(m-1位置)
- 反转时需要记录prev和current指针
- 反转后需要正确连接三部分:
- 反转前的部分(pre)
- 反转后的部分(prev)
- 未反转的部分(current)
4. 常见问题与解决方案
4.1 反转字符串常见问题
问题1:忘记处理空字符串或单字符情况
- 解决方案:虽然代码能正确处理,但面试时应主动说明
问题2:使用额外数组存储反转结果
- 解决方案:题目要求原地修改,必须使用双指针法
问题3:边界条件处理不当
- 解决方案:对于奇数长度字符串,中间元素不需要处理
4.2 反转链表II常见问题
问题1:反转后链表断裂
- 解决方案:确保在反转前记录pre和succ节点位置
问题2:m=1时处理不当
- 解决方案:使用dummy节点统一处理所有情况
问题3:反转区间计算错误
- 解决方案:反转的节点数是n-m+1,循环次数要准确
问题4:指针丢失
- 解决方案:在修改next指针前,先保存下一个节点的引用
5. 复杂度分析与优化
5.1 反转字符串复杂度
- 时间复杂度:O(n),n为字符串长度
- 空间复杂度:O(1),只使用了常数空间
- 优化空间:已经是最优解,无法进一步优化
5.2 反转链表II复杂度
- 时间复杂度:O(n),最多遍历链表两次
- 空间复杂度:O(1),只使用了常数空间
- 优化思路:可以尝试一趟扫描完成,但代码会变得更复杂
6. 实际应用场景
6.1 反转字符串的应用
- 密码学中的简单加密
- 回文串判断的基础
- 字符串匹配算法的预处理
6.2 反转链表II的应用
- 操作系统中的内存块管理
- 数据库中的事务回滚操作
- 文本编辑器的撤销功能实现
7. 扩展练习建议
为了巩固这两个问题的解法,我建议尝试以下变种题目:
- 反转字符串中的单词(LeetCode 151)
- 反转链表中的每K个节点(LeetCode 25)
- 反转字符串中的元音字母(LeetCode 345)
- 反转链表中的偶数节点(自定义题目)
8. 个人心得与建议
在实际练习中,我发现链表问题最容易出错的是指针操作。以下是我总结的几个实用技巧:
- 画图辅助:在纸上画出链表结构和指针变化,能大大降低出错概率
- 边界测试:特别测试m=1和n=链表长度的情况
- 分步验证:先实现完整的链表反转,再扩展到部分反转
- 防御性编程:检查输入是否为空链表或m==n的情况
对于字符串反转,虽然题目简单,但能体现编程基本功。建议:
- 掌握至少两种实现方法(如递归和迭代)
- 理解不同语言中字符串的不可变性
- 思考如何扩展到Unicode字符串的处理
最后,算法能力的提升在于持续练习和总结。代码随想录的这个训练营安排很合理,每天坚持完成作业并记录错题,两个月后会有明显进步。
