wangxl12 / LeetCode

记录力扣刷题进度

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

记录力扣刷题进度。难度中,一颗:star2:表示容易,两颗:star2::star2:表示中等,三颗星:star2::star2::star2:表示困难。FM表示Fully Mastered表示是否完全掌握。

线性表 二叉树 动态规划 贪心 回溯 占位符 占位符 占位符 占位符
🌻 🍀 🌳 🌲 🌵 🌸 🍁 🌴 🌱

🌻 线性表

Problem Difficulty Tags* Methods** Time Space FM Note
21.合并两个有序链表 🌟 链表 1.双指针
2.简化后的代码
3.递归
$O(n)$
$O(n)$
$O(n+m)$
$O(1)$
$O(1)$
$O(n+m)$
注意哨兵节点的灵活使用,可以简化代码。递归思路:每一步合并操作都是将俩子链表较小的一个头结点合并过来,然后处理后续的节点,即head1.val+merge(head1.next, head2) 或者 head2.val+merge(head1, head2.next)
86.单链表的分解 🌟🌟 链表 1.双指针
2.大小链表
$O(n)$
$O(n)$
$O(n+m)$
$O(1)$
$O(1)$
$O(n+m)$
两种方法效率差不多,只不过处理逻辑有差别,双指针逻辑更复杂,双指针用指针从输入链表中取出较小节点插入所维护的链表中;大小链表维护两个链表,分别存储小值的节点和大值的节点,逻辑更容易理解
23.合并k个升序链表 🌟🌟🌟 链表 1.多指针
2.顺序合并
3.分治合并
$O(k^2n+k^2/2)$
$O(k^2n)$
$O(knlogk)$
$O(1)$
$O(1)$
$O(logk)$
多指针维护k个指针分别指向k个链表,循环每次从k个链表中获取当前层的最小值然后插入结果链表,当所有指针为空时退出循环。顺序合并先解决两个顺序链表的合并问题,然后按顺序两两合并。分治合并使用分治算法完成。官方解读还有一种优先队列(二叉堆)的方法,该方法和多指针法很相似,只不过其用一个堆维护k个链表头最小值,而多指针直接用传进来的plist,每次都要选最小值,链表为空时删除。
19.删除链表的倒数第n个节点 🌟🌟 链表 1.O(L)空间复杂度
2.计算链表的长度
3.双指针
$O(L)$
$O(2L-n+1)$
$O(L)$
$O(L)$
$O(1)$
$O(1)$
双指针比较巧妙,两个指针间隔n-1
876.链表的中间节点 🌟 链表
快慢指针
1.双指针
2.快慢指针
$O(n)$
$O(n/2)$
$O(1)$
$O(1)$
双指针的思路是用一个指针一直往后遍历,另一个指针始终指向中间节点;快慢指针的思路是快指针每次跑两个节点,慢指针每次跑一个节点,所以慢指针始终处于中间位置
141.环形链表 🌟 链表
快慢指针
1.快慢指针
2.哈希表
$O(n)$
$O(n)$
$O(1)$
$O(n)$
可证明:慢指针进入环后第一圈内必定与快指针相遇;快指针速度为慢指针的n倍,二者相遇时间不随n增大而减少
142.环形链表II 🌟🌟 链表
快慢指针
1.快慢指针
2.哈希表
$O(n)$
$O(n)$
$O(1)$
$O(n)$
快慢指针**是利用快指针和慢指针可以计算出慢指针距离环的入口的长度,设为c,设链表距离环入口长度为a,可以得到a=(n-1)L+c,也就是说重新定义一个指针从链表头开始走,慢指针继续走下去,二者一定会相遇且第一次相遇点在环的入口处。
160.相交链表 🌟 链表 1.双指针
2.哈希表
$O(n-m+a)$
$O(n+m)$
$O(n)$
$O(1)$
双指针方法较巧妙,先从各自头部开始,短链表结束后从长链表出发,长链表走完后从短链表出发,此时二者剩余步长相等,接下来每一步都判断一下是否相等即可。简化代码可以考虑到,结束程序的时候是二者相等,不论是None还是相交
206.反转链表 🌟 链表 1.栈
2.多指针
3.双指针
4.递归
5.特殊双指针
$O(2n)$
$O(n)$
$O(n)$
$O(n)$
$O(n)$
$O(n)$
$O(1)$
$O(1)$
$O(n)$
$O(1)$
2.多指针和3.双指针思路差不多,但是多指针的实现不够简洁
92.反转链表II 🌟🌟 链表 1.特殊双指针
2.一次遍历
$O(n)$
$O(n)$
$O(1)$
$O(1)$
特殊双指针是基于206.反转链表完成的,先找到需要反转的子链表头结点和尾节点,返回翻转后的链表,再接上。这样方法的缺点是,如果left和right区间很大,则耗时。方法2就是解决这个问题,一次遍历。起初我自己的想法是,到了left开始翻转,记录left前一个节点,反转到right结束,记录right下一个节点,然后拼接。但是官方给的思路是,在翻转区域内,将后一个节点插到翻转区域的头部。这里代码实现的是官方的思路。官方实现的一次遍历在返回结果的时候不用分类讨论,也就是常遇到的特殊情况问题。
24.两两交换链表中的节点 🌟🌟 链表 1.多指针
2.递归
$O(n)$
$O(n)$
$O(1)$
$O(n)$
25.K个一组翻转链表 🌟🌟🌟 链表 1.多指针
$O(n)$ $O(1)$ 此题要先知道接下来是否有k个节点才决定是否翻转。如果不提前计数,最终还需要还原不足的k个节点。一种方式是翻转前统计整个链表节点数,决定需要翻转的子链表数目;一种方式是每次翻转子链表前统计是否够k个。
328.奇偶链表 🌟🌟 链表 1.多指针
$O(n)$ $O(1)$ 用一个计数器判断当前是奇数还是偶数,奇数就将当前节点连接到奇数链表上,偶数就将当前节点连接到偶数链表上。
203.移除链表元素 🌟 链表 1.多指针
$O(n)$ $O(1)$
82.删除排序链表中的重复元素ii 🌟🌟 链表 1.多指针
$O(n)$ $O(1)$
148.排序链表 🌟🌟 链表 1.多指针
$O(n^2)$ $O(1)$ 这种方法无法通过所有案例,最后几个超时
剑指 Offer 22. 链表中倒数第k个节点 🌟 链表 1.双指针
$O(n)$ $O(1)$ 可以使用双指针,快指针提前走K个,然后再一起走,快指针到达链表尾部时,返回慢指针即可

🍀 二叉树

Problem Difficulty Tags* Methods** Time Space FM Note
144.二叉树前序遍历 🌟 二叉树 1.递归
2.循环
3.递归(不依赖语言特性)
$O(n)$
$O(n)$
$O(n)$
$O(logn)$
$O(1)$
$O(n)$
前序先访问根节点,故不需要循环走到底入栈节点。只需每一步先输出根节点的值,然后判断右节点是否为空,入栈,判断左节点是否为空,入栈,进入下一次循环。1.用到了链表的合并,依赖于Python的语言特性,3.用一个全局列表避免列表的合并操作,不依赖于语言特性。
144.二叉树中序遍历 🌟 二叉树 1.递归
2.循环
3.Morris
4.访问标记法
$O(n)$
$O(n)$
$O(n)$
$O(n)$
$O(n)$
$O(n)$
$O(1)$
$O(n)$
2.循环:中序根节点不会马上访问,所以需要入栈,先访问左节点,用一个指针cur指向左节点,循环一直走到其最左边,历经节点入栈,到底后开始出栈,若右节点不为空,修改cur指向右节点。需要注意的是cur不能指向已经入栈过的节点,避免死循环。3.Morris:改法改进循环法,将空间复杂度缩小为O(1),思路是找到根节点左子树最靠右(最后一个访问)的节点,该节点的右趋节点设置为根节点,这样保证了可以回来从而顺利访问右子树。4.访问标记法:访问过的节点用黑色标记,未访问的用白色标记,若节点为白色,将其左右节点以及本身入栈(顺序依据前中后序遍历二叉树而定),若为黑色,输出值。这种方法可以用于前中后序遍历二叉树,且改动少,思路简单。
104. 二叉树的最大深度 🌟 二叉树 1.递归
2.数层数
3.广度优先
$O(n)$
$O(n)$
$O(n)$
$O(height)$
$O(n)$
$O(n)$
数层数法指的是每走一层,将该层所有非空节点存入一个列表,随后将该列表存入总列表,每一次从总列表的最后一个列表中访问内部所有节点是否有左右节点,若有,新建一个列表(新的一层),否则返回总列表的项数(即层数)。3.广度优先:思路和数层数法类似,只是每次将前一层的节点都pop掉,降低了空间复杂度为O(n)的概率
543.二叉树的直径 🌟 二叉树 1.递归
2.
$O(n)$
$O()$
$O(n)$
$O()$
递归方法再好好复习一下
1008.前序遍历构造二叉搜索树 🌟🌟 二叉树 1.递归
2.
$O(n)$
$O()$
$O(n)$
$O()$
搜索树即左子树所有值严格小于根,右子树所有值严格大于根节点。前序遍历序列第一个值为根,所有小于该值的在左子树,大于该值的在右子树,根据这一递归思路可以求解。
105. 从前序与中序遍历序列构造二叉树 🌟🌟 二叉树 1.递归
2.
$O(n)$
$O()$
$O(n)$
$O()$

🌳 动态规划

Problem Difficulty Tags* Methods** Time Space FM Note
🌟 二叉树 1.
2.
$O()$
$O()$
$O()$
$O()$

🌲 贪心

Problem Difficulty Tags* Methods** Time Space FM Note
🌟 二叉树 1.
2.
$O()$
$O()$
$O()$
$O()$

🌵 回溯

Problem Difficulty Tags* Methods** Time Space FM Note
46. 全排列 🌟🌟 回溯 1.回溯
2.优化空间复杂度
$O(n \cdot n!)$
$O(n \cdot n!+2n)$
$O(n \cdot n!)$
$O(n \cdot n!)$
递归算法的用时和内存消耗与递归树的节点数紧密相关,递归树的非叶子结点与叶子结点的行为不同。非叶子结点中,第$i$层递归树结点数为 $A^i_{n}$ ,所以仅仅看递归树的深度,每一个叶子结点的计算用时<2n!,而每一个叶子结点中也循环了n次,所以非叶子结点的时间复杂度为 $O(n \cdot n!)$ 。最后一层有n!个叶子结点,每一个叶子结点都要进行一次拷贝,所以叶子结点的用时也为 $O(n \cdot n!)$ ,所以总的时间复杂度也为这个。对于答案数组,全排列的个数为n!,每一个需要耗费n空间,所以空间复杂度为 $O(n \cdot n!)$ ,非答案数组中,需要存储是否被访问过(n个),以及track(长度为n)。为了减少这两个部分的空间消耗,可以直接原地对nums数组操作(官方解法,即解法2),但是该法的缺点是结果不满足字典序。
78. 子集 🌟🌟 回溯 1.回溯 $O(n2^n)$ $O(n)$ 使用回溯时,画出回溯树,为了不重复,每一个元素之后只访问比其大的元素,给人一种需要对nums先排序的感觉,所以我自己实现的时候对nums排序了一下,而且我递归传入backtrack函数的begin参数为begin+1,而不是i+1;标准的实现不用排序,且传入begin 参数的是i+1.对于复杂度,集合元素个数为 $2^n$ ,即状态个数,每一个状态需要 $O(n)$ 复杂度时间得到。空间复杂度为 $O(n)$ ,递归栈为 $O(n)$,临时状态空间为 $O(n)$

About

记录力扣刷题进度