链接:
🔥 LeetCode 热题 HOT 100 - 力扣(LeetCode)全球极客挚爱的技术成长平台
简单(21道):
160. 相交链表
检查两个链表是否相交
方法一:哈希
首先将A的所有结点存储在哈希表中,接着遍历B,如果B的某个结点存在于哈希表中,则证明相交;
方法二:双指针
指针p1从A的头结点出发,指针p2从B的头结点出发;当p1不为null时,指向下一个;当p1为null时,指向B的开始。p2同理。直到2个指针指向同一个结点(有交点)或者同时指向为null(无交点)。
234. 回文链表
方法一:将值复制到数组中后用双指针法
方法二:快慢指针
指针p1和p2开始都指向第一个结点,然后p1每次移动1步,p2则2步,当p2移到末尾时,则p1到中间位置,接着反转后半部分,并与前半部分比较。
226. 翻转二叉树
方法一:递归
如果左右两棵子树都已实现了反转,那么仅仅交换其位置即可。
206. 反转链表
方法一:递归
在遍历链表时,将当前节点的 next 指针改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。最后返回新的头引用。
169. 多数元素
方法一:哈希
遍历一遍数组nums,存在哈希表中,相同元素+1;接着再遍历一次哈希表,找出最大值
方法二:排序
对数组进行排序,那么下标为 ⌊n/2⌋ 的元素(下标从
0
开始)一定是众数。方法三:随机化
随机挑选一个值,检查其是否是众数;不是则重新随机。
141. 环形链表
方法一:哈希表
从头结点出发,检索当前结点是否在哈希表中,若不再,则加入;若在,则表示有环
方法二:快慢指针
指针p1在头结点处,指针p2在头结点的next处,然后p1每次移动1格,p2则走2步,若在其中一个进入null之前两者指向同一结点,则代表有环。
136. 只出现一次的数字
方法一:集合增删
使用集合存储数字。遍历数组中的每个数字,如果集合中没有该数字,则将该数字加入集合,如果集合中已经有该数字,则将该数字从集合中删除,最后剩下的数字就是只出现一次的数字。
方法二:哈希
使用哈希表存储每个数字和该数字出现的次数。遍历数组即可得到每个数字出现的次数,并更新哈希表,最后遍历哈希表,得到只出现一次的数字。
方法三:集合求和
使用集合存储数组中出现的所有数字,并计算数组中的元素之和。由于集合保证元素无重复,因此计算集合中的所有元素之和的两倍,即为每个元素出现两次的情况下的元素之和。由于数组中只有一个元素出现一次,其余元素都出现两次,因此用集合中的元素之和的两倍减去数组中的元素之和,剩下的数就是数组中只出现一次的数字。
方法四:位运算
因此对于出现次数为偶数的元素,执行连续位运算的最后结果是0,而对于出现次数为奇数的元素,最后当然就被保存下来了。
C++ 提供了 6 种位运算符,包括按位与(&)、按位或(| )、按位异或(^)、取反(~)、左移(<<)、右移(>>)
461. 汉明距离
方法一:使用内置函数
__builtin_popcount(a^b)
方法二:移位实现位计数
while (s) {
ret += s & 1;
s >>= 1;
}方法三:每次都去掉最右侧的1
while (s) {
s &= s - 1;
ret++;
}
448. 找到所有数组中消失的数字
方法一:哈希
方法二:直接对数组进行修改
具体来说,遍历 nums,每遇到一个数 x,就让 nums[x−1] 增加 n。由于 nums 中所有数均在 [1,n] 中,增加以后,这些数必然大于 n。最后我们遍历 nums,若 nums[i] 未大于 n,就说明没有遇到过数 i+1。这样我们就找到了缺失的数字。
338. 比特位计数
方法一: 移位实现位计数
方法二: 每次都去掉最右侧的1
方法三: 动态规划——最高有效位
不同于暴力搜索,动态规划是一种用一个状态代表一类情况的方法。我们观察到对于1011而言,其1的位数其实相当于(1011-1000)中1的位数+1。我们称1、10、100、1000这种2的整数次幂代表最高有效位highBit。状态转移公式为bits[i]=bits[i−highBit]+1。而一个数是否是最高有效位只需要通过i&(i-1)==0判断即可。
方法四: 动态规划——最低有效位
奇数中1的个数 = 偶数(奇数-1)中1的个数 + 1; 例如 111 110
偶数中1的个数 = 偶数/2中1的个数; 例如 1000 100
因此状态转移方程为bits[i]=bits[i>>1]+i&1
方法五: 动态规划——最低设置位
我们观察到对于1011而言,其1的位数相当于去掉最右侧1后1010中1的位数+1。状态转移公式为bits[i]=bits[i&(i-1)]+1。
121. 买卖股票的最佳时机
方法一:一次遍历
假设我们要在第i天卖出股票,那么自然我们希望股票是在这天之前的低谷买入的,因此我们只需要在遍历的同时维护一个前面天中的低谷,不断更新即可。
283. 移动零
方法一:双指针法
使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。
右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。
注意到以下性质:
左指针左边均为非零数;
右指针左边直到左指针处均为零。
因此每次交换,都是将左指针的零与右指针的非零数交换,且非零数的相对顺序并未改变。
543. 二叉树的直径
方法一:DFS
首先我们知道一条路径的长度为该路径经过的节点数减一,所以求直径(即求路径长度的最大值)等效于求路径经过节点数的最大值减一。
而任意一条路径均可以被看作由某个节点为起点,从其左儿子和右儿子向下遍历的路径拼接得到。
21. 合并两个有序链表
方法一:递归(也可以认为是双指针)
20. 有效的括号
方法一:用栈模拟即可
617. 合并二叉树
方法一:DFS
可以使用深度优先搜索合并两个二叉树。从根节点开始同时遍历两个二叉树,并将对应的节点进行合并。
两个二叉树的对应节点可能存在以下三种情况,对于每种情况使用不同的合并方式。
如果两个二叉树的对应节点都为空,则合并后的二叉树的对应节点也为空;
如果两个二叉树的对应节点只有一个为空,则合并后的二叉树的对应节点为其中的非空节点;
如果两个二叉树的对应节点都不为空,则合并后的二叉树的对应节点的值为两个二叉树的对应节点的值之和,此时需要显性合并两个节点。
对一个节点进行合并之后,还要对该节点的左右子树分别进行合并。这是一个递归的过程。
104. 二叉树的最大深度
方法一:DFS
如果我们知道了左子树和右子树的最大深度 l 和 r,那么该二叉树的最大深度即为max(l,r)+1。而左子树和右子树的最大深度又可以以同样的方式进行计算。
因此我们可以用「深度优先搜索」的方法来计算二叉树的最大深度。具体而言,在计算当前二叉树的最大深度时,可以先递归计算出其左子树和右子树的最大深度,然后在 O(1) 时间内计算出当前二叉树的最大深度。递归在访问到空节点时退出。
101. 对称二叉树
方法一:递归
94. 二叉树的中序遍历
方法一:递归
1. 两数之和
方法一:哈希
70. 爬楼梯
方法一:动态规划
方法二:矩阵快速幂
方法三:通项公式
中等(66道):
236. 二叉树的最近公共祖先
方法一:递归
方法二:存储父结点然后向上遍历
739. 每日温度
方法一:暴力
反向遍历温度列表。对于每个元素 temperatures[i],在数组 next 中找到从 temperatures[i] + 1 到 100 中每个温度第一次出现的下标,将其中的最小下标记为 warmerIndex,则 warmerIndex 为下一次温度比当天高的下标。如果 warmerIndex 不为无穷大,则 warmerIndex - i 即为下一次温度比当天高的等待天数,最后令 next[temperatures[i]] = i。
例如,反向遍历开始,对于73而言,从74~100全是无穷大,因此res[7]=0,next[73]=7;接着是76,从77~100全是无穷大,因此res[6]=0,next[76]=6;接着是72,从73~100发现角标最小的是next[76]=6,因此res[5]=next[76]-5=1,next[72]=5,其他以此类推。
方法二:单调栈
当我们聚焦在某个元素x上时,我们只关心在x右侧且比x值要大的离x最近的值。例如75,71,69,72,76,78,对于75而言,我们只关心76的存在,75~76中间的其他值对75都没意义。因此我们可以开辟一个单调栈,从栈底到栈顶的下标对应的温度列表中的温度依次递减。如果一个下标在单调栈里,则表示尚未找到下一次温度更高的下标。
221. 最大正方形
方法一:暴力
将每一个方格分别视为正方形的左上角,如果是0,继续下一个;如果是1,开始尝试延申边长,并加以判断是否满足全为0的条件。
方法二:动态规划
我们用 dp(i,j) 表示以 (i,j) 为右下角,且只包含 1 的正方形的边长最大值。如果我们能计算出所有 dp(i,j) 的值,那么其中的最大值即为矩阵中只包含 1 的正方形的边长最大值,其平方即为最大正方形的面积。
先来阐述简单共识
- 若形成正方形(非单 1),以当前为右下角的视角看,则需要:当前格、上、左、左上都是 1
- 可以换个角度:当前格、上、左、左上都不能受 0 的限制,才能成为正方形
上面详解了 三者取最小 的含义:
图 1:受限于左上的 0
图 2:受限于上边的 0
图 3:受限于左边的 0
数字表示:以此为正方形右下角的最大边长
黄色表示:格子 ? 作为右下角的正方形区域
就像 木桶的短板理论 那样——附近的最小边长,才与 ? 的最长边长有关。如上图所示,状态转移方程为:
注意对于边缘元素(i==0||j==0)要记得初始化为1,否则就执行上述的状态转移方程即可。
215. 数组中的第K个最大元素
方法一:快排(不用sort,手搓找到第k个最大元素即可)
方法二:构建大顶堆,做 k−1 次删除操作后堆顶元素就是我们要找的答案。
208. 实现 Trie (前缀树)
方法一:字典树