优秀的编程知识分享平台

网站首页 > 技术文章 正文

一本或许适合你的LeetCode刷题指南书

nanyue 2025-08-03 07:04:50 技术文章 1 ℃

刷leetcode,没有线索无脑刷,行不行?不行,因为这效率太低下了!题主以前刷leetcode,虽然刷了100多道,但是,过不了一两个月,你就会发现这些题都从你的脑海里消失了,当你需要回忆起相关的问题和章节时,可能很难迅速而高效的找出来。

leetcode官网本身虽然出了一些基于内容的合集,但合集也只是问题的集合,题目和题目之间缺乏链接、递进和讲解,解答的难易程度水平参差不齐。

在这里推荐一个谷歌大佬的刷题笔记,每一道题的题解都写得非常清楚.

以下是常见题型:

作者在美国卡内基梅隆大学攻读硕士学位时,为了准备实习秋招,他从夏天开始整理 Leetcode 上的题目,几个月的时间,刷了几百道题目。

凭借着扎实的基础和长期的勤奋,他很快找到了如愿的工作。

入职前,闲暇的时候,他突然想到,自己刷了那么多题,而且对很多题目的解法有着总结,为何不把这些题目归纳总结一些,做成一个便于后来者阅读学习的电子书呢?

有了想法,作为行动派的他说干就干,于是这样一本制作精美且免费开源的书籍出现在大家面前。

引用他的话来说:

本书分为算法和数据结构两大部分,又细分了十五个章节,详细讲解了刷LeetCode时常用的技巧。我把题目精简到了101道,一是呼应了本书的标题,二是不想让读者阅读和练习时间过长。
这么做不太好的一点是,如果只练习这101道题,读者可能对算法和数据结构的掌握不够扎实。因此在每一章节的末尾,我都加上了一些推荐的练习题,并给出了一些解法提示,希望读者在理解每一章节后把练习题也完成。

我简单总结一下以通俗易懂的方式给大家展示出来

一、算法思想篇

二、数据结构篇

三、高频题型套路

四、避坑指南

五、实战技巧

从我的直观感受来说,这是一本用心的数据结构算法类书籍,全书总共 143 页篇幅,详细讲解算法的内容有十五个章节。

每个章节都是一些重要的知识点,伴有基础讲解和例题介绍,当然,也有一些推荐的练习题。

话不多说,让我们来看一下书的目录:

整本书,我仔细看了一遍,并对书中的一些解题思路和代码进行校验。

从我的直观感受来说,这是一本用心的数据结构算法类书籍,全书总共 143 页篇幅,详细讲解算法的内容有十五个章节。

每个章节都是一些重要的知识点,伴有基础讲解和例题介绍,当然,也有一些推荐的练习题。

因为页数原因,我就专挑重要的部分给大家展示,有需要全部内容的私信小编学习,即可免费领取哦!

贪心算法

顾名思义,贪心算法或贪心思想采用贪心的策略,保证每次操作都是局部最优的,从而使最

后得到的结果是全局最优的。

举一个最简单的例子:小明和小王喜欢吃苹果,小明可以吃五个,小王可以吃三个。已知苹

果园里有吃不完的苹果,求小明和小王一共最多吃多少个苹果。在这个例子中,我们可以选用的

贪心策略为,每个人吃自己能吃的最多数量的苹果,这在每个人身上都是局部最优的。又因为全

局结果是局部结果的简单求和,且局部结果互不相干,因此局部最优的策略也同样是全局最优的

策略。

在贪心算法下有两个重点问题

分配问题

区间问题

现在到了双指针

双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。也可以延伸到多个数组的多个指针。

若两个指针指向同一数组,遍历方向相同且不会相交,则也称为滑动窗口(两个指针包围的区域即为当前的窗口),经常用于区间搜索。

若两个指针指向同一数组,但是遍历方向相反,则可以用来进行搜索,待搜索的数组往往是排好序的。

对于 C++ 语言,指针还可以玩出很多新的花样。一些常见的关于指针的操作如下。

同样的还是两个经典问题抛给大家,供大家参考

归并两个有序数组

滑动窗口

二分查找

二分查找也常被称为二分法或者折半查找,每次查找时通过将待查找区间分成两部分并只取一部分继续查找,将查找的复杂度大大减少。对于一个长度为 O(n) 的数组,二分查找的时间复杂度为 O(log n)。

举例来说,给定一个排好序的数组 {3,4,5,6,7},我们希望查找 4 在不在这个数组内。第一次折半时考虑中位数 5,因为 5 大于 4, 所以如果 4 存在于这个数组,那么其必定存在于 5 左边这一半。于是我们的查找区间变成了 {3,4,5}。(注意,根据具体情况和您的刷题习惯,这里的 5 可以保留也可以不保留,并不影响时间复杂度的级别。)第二次折半时考虑新的中位数 4,正好是我们需要查找的数字。于是我们发现,对于一个长度为 5 的数组,我们只进行了 2 次查找。如果是遍历数组,最坏的情况则需要查找 5 次。

我们也可以用更加数学的方式定义二分查找。给定一个在 [a; b] 区间内的单调函数 f (x),若f (a) 和 f (b) 正负性相反,那么必定存在一个解 c,使得 f (c) = 0。在上个例子中,f (x) 是离散函数f (x) = x +2,查找 4 是否存在等价于求 f (x) -4 = 0 是否有离散解。因为 f (1) -4 = 3-4 = -1 < 0、f (5) - 4 = 7 - 4 = 3 > 0,且函数在区间内单调递增,因此我们可以利用二分查找求解。如果最后二分到了不能再分的情况,如只剩一个数字,且剩余区间里不存在满足条件的解,则说明不存在离散解,即 4 不在这个数组内。

具体到代码上,二分查找时区间的左右端取开区间还是闭区间在绝大多数时候都可以,因此有些初学者会容易搞不清楚如何定义区间开闭性。这里我提供两个小诀窍,第一是尝试熟练使用一种写法,比如左闭右开(满足 C++、Python 等语言的习惯)或左闭右闭(便于处理边界条件),尽量只保持这一种写法;第二是在刷题时思考如果最后区间只剩下一个数或者两个数,自己的写法是否会陷入死循环,如果某种写法无法跳出死循环,则考虑尝试另一种写法。

二分查找也可以看作双指针的一种特殊情况,但我们一般会将二者区分。双指针类型的题,指针通常是一步一步移动的,而在二分查找里,指针每次移动半个区间长度。

同样的还是两个经典问题抛给大家,供大家参考

第一个就是求开方

给定一个非负整数,求它的开方,向下取整。

第二个就是查找区间

现在到了排序算法

以下是一些最基本的排序算法。虽然在 C++ 里可以通过 std::sort() 快速排序,而且刷题时很少需要自己手写排序算法,但是熟习各种排序算法可以加深自己对算法的基本理解,以及解出由这些排序算法引申出来的题目。

有以下5种排序类型他们分别是快速排序、归并排序、插入排序、冒泡排序、选择排序

在这一个经典问题抛给大家,供大家参考

由于中间包含的算法类型实在是太多,我就跳过,直接来描述数据结构

指针三剑客之一:链表

(单)链表是由节点和指针构成的数据结构,每个节点存有一个值,和一个指向下一个节点的指针,因此很多链表问题可以用递归来处理。不同于数组,链表并不能直接获取任意节点的值,必须要通过指针找到该节点后才能获取其值。同理,在未遍历到链表结尾时,我们也无法知道链表的长度,除非依赖其他数据结构储存长度。LeetCode 默认的链表表示方法如下。

struct ListNode {

int val;

ListNode *next;

ListNode(int x) : val(x), next(nullptr) {}

};

由于在进行链表操作时,尤其是删除节点时,经常会因为对当前节点进行操作而导致内存或指针出现问题。有两个小技巧可以解决这个问题:一是尽量处理当前节点的下一个节点而非当前节点本身,二是建立一个虚拟节点 (dummy node),使其指向当前链表的头节点,这样即使原链表所有节点全被删除,也会有一个 dummy 存在,返回 dummy->next 即可。

案例题我直接给大家甩出来

以 O(1) 的空间复杂度,判断链表是否回文。

指针三剑客之二:树

作为(单)链表的升级版,我们通常接触的树都是二叉树(binary tree),即每个节点最多有两个子节点;且除非题目说明,默认树中不存在循环结构。LeetCode 默认的树表示方法如下。

struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

可以看出,其与链表的主要差别就是多了一个子节点的指针。

数的递归

对于一些简单的递归题,某些 LeetCode 达人喜欢写 one-line code,即用一行代码解决问题,把 if-else 判断语句压缩成问号冒号的形式。我们也会展示一些这样的代码,但是对于新手,笔者仍然建议您使用 if-else 判断语句。在很多时候,树递归的写法与深度优先搜索的递归写法相同,因此本书不会区分二者

案例题我直接给大家甩出来


指针三剑客之三:图

作为指针三剑客之三,图是树的升级版。图通常分为有向(directed)或无向(undirected),有循环(cyclic)或无循环(acyclic),所有节点相连(connected)或不相连(disconnected)。树即是一个相连的无向无环图,而另一种很常见的图是有向无环图(Directed Acyclic Graph,DAG)。


图通常有两种表示方法。假设图中一共有 n 个节点、m 条边。第一种表示方法是邻接矩阵(adjacency matrix):我们可以建立一个 n× n 的矩阵 G,如果第 i 个节点连向第 j 个节点,则 G[i][j]= 1,反之为 0;如果图是无向的,则这个矩阵一定是对称矩阵,即 G[i][j] = G[j][i]。第二种表示方法是邻接链表(adjacency list):我们可以建立一个大小为 n 的数组,每个位置 i 储存一个数组或者链表,表示第 i 个节点连向的其它节点。邻接矩阵空间开销比邻接链表大,但是邻接链表不支持快速查找 i 和 j 是否相连,因此两种表示方法可以根据题目需要适当选择。除此之外,我们也可以直接用一个 m × 2 的矩阵储存所有的边。

案例题我直接给大家甩出来

练习可以看着练习,也可直接搜索答案直接查询,对自己的提升也是有好处的

因为包含的内容比较多,这里只做了简单的章节截图介绍,每个章节都有更加细节化的知识点,小编已经整理成册;

需要拿来学习的小伙伴,私信小编【学习】来获取!

希望能够帮助到大家的学习!

Tags:

最近发表
标签列表