力扣每日一题 2021/4

2021-04-01
4/1 笨阶乘

难度 MEDI 链接

今天是愚人节,我直呼上当了。。写了个每四个一计算再累加的O(n)算法,然而没想到最终结果是可以直接用表达式的。。也就是O(1)。。

4/2 直方图的水量

难度 HARD 链接

以前算法课做过,然而只是大概记得一点。。最重要的是:一个点,左侧最大<右侧最大,则该位置存水量=左侧最大-自己,另一种情况也一样。

然后就可以用两个指针(指向当前左、右位置)和两个值(表示当前左最大和右最大)来实现了。

4/3 最长公共子序列

难度 MEDI 链接

经典dp题目,转移方程是:

$dp[i][j] = dp[i-1][j-1]+1,\ if\ s[i]=t[j]$

$dp[i][j] = MAX{dp[i-1][j],dp[i][j-2]},\ if\ s[i]!=t[j]$

比较容易理解。

4/4 森林中的兔子

难度 MEDI 链接

很简单的题目,不过倒是让我知道了C++里的hash容器unordered_map。

4/5 合并两个有序数组

难度 EASY 链接

没啥好说的。这两天可能是双指针专题。

4/6 删除有序数组中的重复项 II

难度 MEDI 链接

确实又是双指针。。不过我没想到,用的是简单一点的两次遍历的方法,第一次标记重复的,第二次移动,但是其实可以放在一个遍历里使用双指针完成。具体做法就是第一个指针指向接下来遍历到的数字,第二个指针指向接下来需要前移的数字前移的位置(当nums[i] != nums[i-2]时就需要前移)。

4/7 搜索旋转排序数组 II

难度 MEDI 链接

旋转排序其实就是两段非降序数组拼接,并且第一个的最小值(left)大于等于第二个的最大值(right),如下图。

那么在两种情况下是有序的,即mid在分割点的左边,则[left:mid]有序,若此时nums[left] <= target < nums[mid] 就可以使用二分法;mid在分割点的右边,则[mid:right]有序,若此时nums[right] >= target > nums[mid]就可以使用二分法。否则则去掉这一半,依然起到二分法作用。

由于非降序,只有一种情况下不是二分,即nums[left] == nums[mid],此时无法判断属于那种情况,只能left++。

4/8 寻找旋转排序数组中的最小值

难度 MEDI 链接

和昨天的基本一个思路,不过由于元素各不相同判断要简单一点,当nums[left] <= nums[mid] 时就说明这一部分有序,此时nums[left]就是这部分最小值,接下来去剩下的继续找就行;另一种情况也一样。

4/9 寻找旋转排序数组中的最小值 II

难度 HARD 链接

改成了可重复,那么就用4.7.的方法,在最坏情况会退化为O(n)。

4/10 丑数

难度 EASY 链接

简单,不说了。。。

4/11 丑数 II

难度 MEDI 链接

先创建一个大小为n的数组vec,vec[0]为1.然后有三个指针指向当前选用的*2、*3、*5的备选数,初始指向vec[0],之后,每次将三者中最小的那个作为vec[i]并将其指针数+1。这样,下一个要填的数比三者当前指向的大,比三者*相应倍数的小(或等于),那么就是下一个满足条件的数。如果有重复,比如6=2×3=3×2,那么这时候两个指针指向的都是最小的,将两个都加1即可。

4/12 最大数

难度 MEDI 链接

我的做法是将其全部转化为string后排序,排序使用的规则为:

bool mygreater(string a, string b){
    return a+b > b+a;
}

这样排序过后就直接按顺序增加到结果string中即可。

4/13 二叉搜索树节点最小距离

难度 EASY 链接

由于是二叉搜索树,故中序遍历即可,因为这样得到的顺序就是从大到小的顺序,最小差值就出现在相邻的数值中间。

4/14 实现 Trie (前缀树)

难度 MEDI 链接

前缀树早就忘了。。所以先上网看了看。在理解前缀树的定义之后写起来还是比较简单的。

4/15 打家劫舍 II

难度 MEDI 链接

本来以为还是vim,没想到是dp。一开始没有想清楚首尾相连的怎么处理,看了下评论,可以分成选第一个不选最后一个和选最后一个不选第一个两种情况形成两个队列,分别求其最大值。

转移方程很简单,$dp[i] = max(dp[i-1],dp[i-2]+nums[i])$

4/16 扰乱字符串

难度 HARD 链接

今天的题目很难,只是迷迷糊糊知道大概怎么做,最后参考题解的。

其实就是dp,转移方程其实很好写,比较困难的点在于如何使用这个转移方程。学到了新的dp的方法,就是记忆化+递归搜索,用数组在每次递归得到子结果时将子结果存在dp数组里,并在递归开始时先查看dp数组有没有想要的结果,这样就实现了递归的dp。

4/17 存在重复元素 III

难度 MEDI 链接

既然是滑动窗口形式,就可以考虑用桶排序,桶的大小为t-1,那么同一个桶的元素就符合要求。因此,循环时一个桶只会有一个元素,因为发现另一个时直接返回true。在每次循环时,先看桶内有无,无的情况下看相邻桶有无,若有与之比较即可。注意取值遍布int的上下限,我这里为了避免溢出问题使用了long。在循环大于k时记得擦除i-k时存储的数字。

4/18 删除有序数组中的重复项

难度 EASY 链接

简单题,没什么好说的。

4/19 移除元素

难度 EASY 链接

又是简单题,没什么好说的。

4/20 实现 strStr()

难度 EASY 链接

虽然难度是简单,但其实是重新学习了一下早就忘了的KMP算法,贴一个说的很好的教程

4/21 解码方法

难度 MEDI 链接

比较常规的dp题,注意一下字母的判断即可。

4/22 矩形区域不超过 K 的最大数值和

难度 HARD 链接

挺难的,再次看了题解,方法是二分的思路来降低穷举的复杂度,即固定上下或左右边,然后二分来处理剩下的。看了题解和【宫水三叶】优化枚举的基本思路 & 将二维抽象成一维 & 最大化「二分」效益 & 空间优化也只能说勉强理解。。

4/23 最大整除子集

难度 MEDI 链接

找最大子集个数的过程比较简单,做法是dp,dp数组记录以当前nums数组值为最大值的整除子集,转移方程就是$dp[i]\ =\ max(dp[i],\ dp[j]\ +\ 1)$,其中j是0到i之间所有满足$nums[i]\ \%\ nums[j] == 0$的整数,这个做法就要求数组有序,所以先要调用一次排序算法。

获得子集的方法,我一开始打算并查集实现,不过感觉比较麻烦就看了题解,题解的方法是倒推,就是相当于把dp的过程倒了过来,思路挺好。

4/24 组合总和 Ⅳ

难度 MEDI 链接

又是dp,这次的dp[i]并不是表示使用nums中0到i个能组成target元素的个数,因为这样dp[i]和dp[i+1]的关系就很复杂,可能需要去掉多个0到i的元素加入i+1元素,也可能只去掉一个或不去掉等等等等。这次,dp[i]表示整个数组中总和为i的组合数,即结果存放在dp[target]中。这样,dp[i]的转移方程就是j遍历0:i-1,若$nums[j] <= i$则$dp[i] += dp[i-nums[j]]$。

4/25 递增顺序搜索树

难度 EASY 链接

终于迎来了简单题。。题目都说了用中序遍历,遍历中改一下节点指向就行。

4/26 在 D 天内送达包裹的能力

难度 MEDI 链接

只想出了基础的解法,没想到题解里就是基础的解法。

为了运下包裹,运载能力最低为最大的包裹值,最高为包裹总重,做一个二分查找即可。

4/27 二叉搜索树的范围和

难度 EASY 链接

简单题,不过比较好奇的是利用二叉搜索树剪枝了一下反而运行时间变久了。。

4/28 平方数之和

难度 MEDI 链接

挺简单的题目,在0到√c中依次尝试即可。不过看了下题解,双指针的思路挺好的。

4/29 青蛙过河

难度 HARD (https://leetcode-cn.com/problems/frog-jump/)[链接]

比较难,用的dp,提交错误修改了很多次。。

4/30 只出现一次的数字 II

难度 MEDI 链接

属实没想到空间复杂度为1的方法,看了题解整傻了

本文阅读量: