刷题论 01|为什么链表题这么难?

383 阅读1分钟

本文为视频的原稿,访问 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 站说过的题,欢迎大家去我的主页刷题。

课外练习