注册一亩三分地论坛,查看更多干货!
您需要 登录 才可以下载或查看附件。没有帐号?注册账号 
x
本帖最后由 jayk666 于 2021-6-6 03:16 编辑
背景:
转码选手,工作经验总共7年 偏前端,相当于CS本科毕业工作5年吧。Leetcode只刷了200+题,所以大牛们可以关闭此页了。一共刷了2遍,第一次是5年前,第二次是今年3月重新刷起,直到5月拿了offer。我自己的经验是,刷题就是一个在大脑里画图的过程。一开始刷比较艰难,刷到100题左右脑袋里开始有图浮现,到现在看完一道题就在脑袋里用图解,最后图画完了 题目也就解出来了。
因为从来没有看到过类似的刷题总结,所以想自己写一篇,如果能早些看到应该能省一半刷题时间。可能你的方法跟我不一样,欢迎讨论。
适用读者:
觉得刷题有难度,medium都很吃力;逻辑能力稍弱;刚转码选手;刷了大量题但没有总结思路的人
希望此文可以帮到你
废话不多,接下来图文解析经典题型↓↓ 喜欢的话别忘了点个赞,能送点米就更好了!
注:所有代码都是伪代码
题型1:Linked List
题目链接:https://leetcode.com/problems/reverse-linked-list/
先拿最简单的练练手,先看参数设置:
c - 传入的head
p - preHead,一般初始会定义一个preHead = new Node(0, head),即它的next为head。最后return p.next就行了
n - c.next
肯定需要遍历一遍才能reverse inplace。每次分4步,基本原理是将一个node的箭头指向另一个地方。
(1) 初始(确认n存在)
(2) 把p.next指向n
(3) 把c.next指向n.next
(4) 把n.next指向c
最后使n = c.next就能开始下一轮循环了。
是不是很简单呢,接下来加大难度
题型2:backtrack or DFS/BFS
题目链接:https://leetcode.com/problems/subsets/
backtrack其实涵盖很多,其中DFS最为经典,来看这道subsets。
首先先用数学的方法分析一下,最后结果有多少子集(输入n)?
答案是:nC0 + nC1 + nC2 + nC3 + ... = 2^n
简单的for/while循环只能整n遍,所以看到这个数字你想啊,两层循环才n^2,肯定搞不定啊。所以要用backtrack,我想称之为套娃。来画个图吧,数字代表index从1开始:(有点乱,蓝色的部分最后看)
我们再来看代码怎么实现的呢,如下currentArray代表上图中箭头旁的array:[1], [1,2], [1,3] ....
- function 套娃(nums, index, result, currentArray) {
- result.push(currentArray) // <----- 套大娃进最终结果
- for (let i = index ; i < nums.length ; i++) {
- currentArray.push(nums) // <----- 把这个小娃套进去
- dfs(nums, i + 1, result, currentArray) // <----- 打包成2->n, 3->n....
- currentArray.pop() // <----- 把上面套进去的小娃拿出来
- // follow up: 如果nums里包含重复元素,那if 这里nums == nums[i-1],则直接continue。想一下为什么
- }
- }
- function subsets(nums) {
- Array<Array<Integer>> result = new Array<>();
- 套娃(nums, 0, result, [])
- return result
- };
复制代码
配合图形应该不难脑补出整个过程。。。以后如果碰到变种题,只要想象在图中哪块区域操作一下就能解决了。
题型3:tree node
题目链接:https://leetcode.com/problems/binary-tree-inorder-traversal/
https://leetcode.com/problems/binary-tree-preorder-traversal/
https://leetcode.com/problems/binary-tree-postorder-traversal/
如果你能轻松的理解第二题,那么这里大概可以跳过了。
对于tree类型的题,只要理解了本质,那么各类变种都是千篇一律。这次我们先看代码:
- function travel(root) {
- // 判断是否为空
- if (!root) {
- return
- }
- travel(root.left)
- travel(root.right)
- }
- function solution(root) {
- travel(root)
- reuturn ...
- }
复制代码
再来看图,标注的方块index为上面代码遍历的顺序
所以现在我们已经有了怎么遍历tree的方法,想求inorder, preorder, postorder,怎么办呢?小修改下代码:
- function travel(root) {
- // 判断是否为空
- if (!root) {
- return
- }
- // <------ 想要preorder?在这里加
- travel(root.left)
- // <------ 想要inorder?在这里加
- travel(root.right)
- // <------ 想要postorder?在这里加
- }
- // <------ 想要成为rapstar?那你来错地方了,咳咳。。。
复制代码
题型4:dp
题目链接:https://leetcode.com/problems/longest-palindromic-substring/
不知道在哪看过这么一句话,跟string有关的问题,基本都能用dp解。
这道题求最长回文。判断一段string是不是回文复杂度O(n),在一段string内求所有substring复杂度为O(n^2),所以暴力解法复杂度为O(n^3)。于是,只要争取把复杂度优化到O(n^2)就算成功了。都是普通人,我们不考虑马拉车解法。。
用dp前,分析下能不能找到通项公式。我假设大家都学过高中数列哈,就是找n-1项与n项之间的关系。
一维: A = f(A[i - 1])
二维(可能): A[j] = f(A[i - 1][j - 1])
....
能通过题目把概念化的dp带入A就行了。
再来看题,dp最好能代表一段substring是不是回文。
一维dp是不是不够用啊?用一维的话 i 代表什么好像都不对,
那就定义二维dp[j]。i,j分别代表substring的头跟尾index。然后看图说话:
以上,需要循环2遍,从j=0~n-1;i=0~j,因为i一直在j前面,复杂度O(n^2)。得出一个完整dp,里面储存了i~j是否为回文的信息。
如何求得最大值?我们需要对该dp再次遍历,找出最长,并且记录i, j。无法脑补的同学看下图(这里i在j前面,所以只有斜半区有值,且对角线上的值都为true):
这次遍历又是O(n^2),达到了预期,代码没什么花头就略了。
总结:
刷题只是敲门砖,我们大部分都是普通人,没必要去追求竞赛类的难度,舍本求末。但刷题确实有助于锻炼我们逻辑思维的能力,这是码农必不可缺的一堂课。可能得益于多年前端的经验,把算法图像化对我来讲比较容易。因为拿到offer,所以现在我的刷题之路就暂停了。大家如果有特别想图像化的题,可以留下评论,有时间的话我可以看看。
一米值千金!
|