本文为视频的原稿,访问 B 站看视频,通过视频更能领会要义哦!
链表是一个基础的数据结构,相信大家都有所了解。
然而,为什么链表题不容易写对? 反转链表、合并有序链表这些题,在面试中能写对的人寥寥。
链表题的难点
容易被指针的 next 绕晕,next.next 等,比如下面这段 JS 代码,是不是看晕了。
for (cur.next && cur.next.next) {
first := cur.next;
second := cur.next.next;
first.next = second.next;
cur.next = second;
cur.next.next = first;
cur = cur.next.next;
}
解题的四大要点
一、理解指针的含义
链表上节点的 next 指针,存储的是下个节点的内存地址。链表的增删改查与链表的 next 指针息息相关,所以需要好好掌握。
// 变量赋值给指针,表示 p 的 next 存储了 q 的内存地址
p.next = q
// 插入
// head => b, head => c => b
c.next = head.next
head.next = c
// 删除
p.next = p.next.next
// 遍历,与数组不同的是,需要根据指针去找下一个节点
for head != nil {
head = head.next
}
二、改变引用前,记得缓存
// 反转链表
// a b => c
// a <= b c
next := b.next
b.next = a
三、技巧:fakeHead
声明一个指向链表头部的空节点 p,即 fakeHead,使其 p.next = head。当链表内部有变化,结果却需要返回完整链表时,就用到了 fakeHead,我们只需返回 p.next 即可。
// 合并有序链表,k个一组反转链表,删除倒数第n个节点
p.next = head
for (head != nil) {
// 遍历链表,删除、插入等操作
head = head.next
}
return p.next
四、易错细节:检查边界条件
- 链表为空、链表只有 1 个节点
- 双向链表,注意头节点、尾节点,是否正常
链表与数组对比
- 数组,存储于连续空间,访问更高效,根据 index 可以在 O(1) 内查到。
- 链表,存储于零散的空间,扩容更简单。数组需要动态扩容。
LeetCode 实战
LeetBook:leetcode.cn/leetbook/de…
讲过的题
这些题是我在 B 站说过的题,欢迎大家去我的主页刷题。