力扣每日一题 2021/7

2021-07-01
7/1 传递信息

难度 EASY

用BFS来做的话就是简单题,我也是这么写的。。这种方法略去不提。但是这道题也可以用DP来做。

定义动态规划的状态 $dp[i][j]$为经过 i 轮传递到编号 j 的玩家的方案数,$dp[k][n−1]$即为最终答案,状态转移方程这样得到:如果第 i 轮传递到编号 src 的玩家,则第 i+1 轮可以从编号 src 的玩家传递到编号 dst 的玩家。

7/2 雪糕的最大数量

难度 MEDI

比较怪的题,一开始我以为是背包问题,但是感觉不对劲,如果是背包,应该最后要求雪糕价值最高而不是数量最多。换句话说,无论雪糕价格,其对最终答案的贡献都为1,那么显然应该贪心得到答案,因为所有雪糕都一样,所以优先选最便宜的。调用库函数排序然后贪心即可。

7/3 根据字符出现频率排序

难度 MEDI

我是用了桶排序来做,不过比之题解有两个未优化的地方:

  1. 由于char范围为0-256,我是开了个256大小的数组来统计词频,题解使用了哈希表,可以节省空间,也更方便书写
  2. 统计完词频后,实际上只需要开大小为最大频率的桶,但是我开了大小为字符串长度的桶,浪费了一定空间

这些是需要注意的地方

7/4 错误的集合

难度 EASY

用的是哈希表,统计总和和一个重复数字,然后计算出应该的正确数字。

题解给了一个空间复杂度O(1)的算法,比较难懂,不看了。

7/5 原子的数量

难度 HARD

与其说是算法题,不如说是工程题。整体难度不大,构造一个自动机即可,需要注意括号的处理。我用了递归,函数参数是哈希表,可以改用存放哈希表的栈从而避免递归。

7/6 点菜展示表

难度 MEDI

又是工程题。用了map(存放桌号和本桌点的菜), set(存放菜名)来避免排序。map的value是一个哈希表,记录点了哪些菜以及多少道。输出的时候遍历set去找本桌的哈希表里有没有这道菜就行。这样做主要是不能提前知道一共多少道菜。

7/7 大餐计数

难度 MEDI

一开始用了哈希表减少重复的O(n^2)算法,还是超时了。正确的做法是每次遍历时查找哈希表内有没有[2的某次幂 - 当前值]这个key。这样把复杂度从O(n*n)降低到O(n*2的幂个数),对于题目的范围是21个。

7/8 和相同的二元子数组

难度 MEDI

子数组和为goal即[0, j]的前缀和与[0, i]的前缀和之差为goal。用哈希表记录前缀和即可。

7/9 主要元素

难度 EASY

不考虑空间复杂度的话用哈希表很容易。考虑的话我只记得算法课讲过。。但是忘了该怎么做。

正确的方法是摩尔计数法,基于这样的原理:如果存在这样的数,这个数与其他数两两相消后数组中最终剩下的还是这个数。

实际操作中遍历一遍,初始时选择第一个数作为假设结果。每遇到这个数值,alive++,否则–(相消)。当alive = -1时,即这个数不可能是主要元素,且真正的主要元素(如果存在)在剩余数组中依然是主要元素。那么把假设结果设为当前值,alive设为1,继续即可。

找到后要重新遍历再检查,因为可能不存在主要元素。

7/10 基于时间的键值存储

难度 MEDI

我一开始没看到时间戳 timestamps 都是严格递增的,故用了map作为哈希表的value。实际上可以使用vector,然后查找的时候使用二分查找,这样应该会快一点。

题解里有个细节:二分查找的时候使用auto &pairs = m[key];加引号是为了避免vector的复制导致时间成本的增加。

7/11 H 指数

难度 MEDI

题目描述怪怪的。应该是满足 “其余的 N - h 篇论文每篇被引用次数 不超过 h 次” 这个要求的优先级要比 “总共有 h 篇论文分别被引用了至少 h 次” 高的意思。

由于h指数有上限,即论文总数,故可以开如此大小的数组,用计数排序降低时间复杂度。

7/12 H 指数 II

难度 MEDI

既然已经排好序,那么就二分查找即可。

7/13 天际线问题

难度 HARD

好难。看了题解才会做。这道题是扫描线问题的一种,关于扫描线问题之后要看一下线段树,很久没做已经忘了。

这道题的核心思想是维护一个优先队列。在扫描到屋顶横线左端时将高度入队,扫描到右端时将高度出队。如果每次扫描到新的点时最大高度发生了变化,那么就产生了新的关键点。

7/14 绝对差值和

难度 MEDI

这道题一开始理解不够,用了贪心的方法,误以为把差值最大的换了就行,但是实际上由于必须替换成数组内的另一元素,所以并不是换最大的。所以这道题的做法是:复制nums1得到nums1s并排序,之后遍历nums2,每个都到nums1s里二分去找,最终找到替换前-替换后最大的。

需要注意的是,由于nums1中可能不存在nums2中的数(二分查找未找到数值),此时查找后的指针指向的数是lower_bound,需要比较该数和该数的下一个数。//代码第30行

7/15 减小和重新排列数组后的最大元素

难度 MEDI

贪心就行,排序后,每次arr[i] = min(arr[i], arr[i-1] + 1)

题解中给了计数排序的方法,由于最优情况是n,故超过n的都可以视作n(至少减小到n) ,在这个前提下,先统计1到n的个数得到cnt数组,然后遍历cnt数组,为0时miss++(需要后续补上),否则miss -= (cnt[i] - 1),最终的答案即为n-miss。

7/16 在排序数组中查找数字 I

难度 EASY

我的做法是二分找到后向左向右统计,这样的最坏情况复杂度是O(n)

正确解法应该是一次二分找到最左,一次二分找到最右

7/17 连续子数组的最大和

难度 EASY

老题目了,基于这样的动态规划:f(i)=max{f(i−1)+nums[i],nums[i]}。

7/18 变位词组

难度 MEDI

用了哈希表,key是一个26位长的string,用来统计每个字符出现了多少次(这里其实有个漏洞就是次数不能超过256,但是就算是int数组也有个上限。。)

7/19 最高频元素的频数

难度 MEDI

这道题没有想到办法,故看题解。。思路非常棒的滑动窗口

7/20 数组中最大数对和的最小值

难度 MEDI

排序后第k大和第k小组合,统计最小值即可。

7/21 两个链表的第一个公共节点

难度 EASY

之前做过。若重复则可分为A+C和B+C,第一个指针从链表1开始,走完后走链表2,第二个指针恰恰相反。若有重复,第一个走到A+C+B,第二个走到B+C+A后在第一个公共节点相遇。

7/22 复制带随机指针的链表

难度 MEDI

对于有随机指针的深拷贝就不能简单地一遍循环搞定(random指向的此时可能还未创建)。我的做法是用哈希表存了random的关系,答案还提供了一种降低空间复杂度的方法,先把A-B-C链变成A-A’-B-B’-C-C’,每个拷贝节点跟在原节点后面,就能通过一次循环找到对应random的位置(原节点random的next),然后再拆分即可。

7/23 检查是否区域内所有整数都被覆盖

难度 EASY

因为大小不超过50,我的方法就是创建一个大小50的数组,遍历每个range,把覆盖到的置true,再遍历检查有无false即可。题解有一种时间复杂度更低的做法:用差分数组 diff 维护相邻两个整数的被覆盖区间数量变化量,其中 diff[i] 对应覆盖整数 i 的区间数量相对于覆盖 i - 1的区间数量变化量。统计时,遍历到range=[l, r]时,diff[l] ++, diff[r+1] –。之后通过前缀和就能得到每个整数有多少区间。

7/24 替换隐藏数字得到的最晚时间

难度 EASY

没什么好说的。一堆if即可。

7/25 从相邻元素对还原数组

难度 MEDI

用哈希表存储所有元素的相邻元素,由于至多有两个,故key为int而value为vector。存储后找到一个相邻元素只有1的(开头或结尾)作为begin,顺着往下还原数组。

7/26 得到子序列的最少操作次数

难度 HARD

我原先的想法是dp,dp[i][j]表示在arr的j位置前插入target[i],得到前i个target的子序列的操作次数。然而这样的定义导致了很多问题,因为j位置可能就是target[i]而导致一系列需要调整的地方,最后越写越乱。

其实这道题换个思路来看就是最长公共子序列问题。记数组 target 的长度为 n,数组 arr 的长度为 m。最少添加的元素个数为 n 减去两数组的最长公共子序列的长度。但是在本题的限制下,常规的O(nm)的最长公共子序列的dp算法不能通过,需要对其进行空间换时间。

通过引入哈希表pos,pos[target[i]] = i,我们把arr的数分为了:1. 在pos中,即该数出现在arr的第i个位置上;2. 不在pos中。这样,原先的数值就不重要了,target相当于变成了0,1,2,……的递增数组,arr变成了类似0,3,-1,2,-1……这样的数组,其中-1表示不在target中出现,其他值表示出现的位置。问题转换为求最长递增子序列。它的做法是贪心+二分查找:建立数组d,从前往后遍历arr,如果在pos中才进行以下操作:

利用二分查找找到d中大于等于pos[i]的第一个数(lower_bound),如果找到了,把它更新为pos[i],这样,递增子序列就会增长得更慢,得到的结果就会更优。如果没找到,说明都小于pos[i],那么递增子序列长度就能+1,把pos[i]添加到d的结尾。

7/27 二叉树中第二小的节点

难度 EASY

这个特殊二叉树的根必然是最小的,dfs找到第二小的即可。

7/28 二叉树中所有距离为 K 的结点

难度 MEDI

第一次dfs,用哈希表存下每个节点的父亲,第二次dfs就可以从target开始,dfs到k层就添加到结果中。第二次的dfs有左子、右子、父三个出路,选择不是自己来的那两条路递归。

7/29 二叉树寻路

难度 MEDI

数学题,先得到层数,再从后往前,根据层的奇偶和子节点数值得到自己的数值。

7/30 Excel 表列序号

难度 EASY

做过的简单题。进制转换问题。

7/31 二叉树的垂序遍历

难度 HARD

最不困难的困难。。先遍历一次,用map记录(col, row)位置的所有结点,然后遍历map整理成答案所需格式即可。

本文阅读量: