顺利保研,国庆期间觉得还是需要练练每日一题保持手感。
10/4 密钥格式化
难度 EASY
由于是第一个小于等于规定数目,故倒着处理即可。
10/5 窥探迭代器
难度 MEDI
我的评价是不知道这题想干啥,参考了题解。。要善用题目提供的接口。
10/6 第三大的数
难度 EASY
用大小为3的数组记录就行。
10/7 字符串中的单词数
难度 EASY
难道是因为国庆,题目都简单了?注意可能有连续空格就行。
10/8 重复的DNA序列
难度 MEDI
既然确定长度是10,就用一个sliding window+哈希表处理即可。
10/9 将数据流变为多个不相交区间
难度 HARD
题目相对比较复杂,但难度不算太大。
本题的数据结构核心是一个map,key为某个区间起点,value为终点。每次执行void addNum(int val)
时,使用upper_bound找到比val大的第一个区间对应迭代器 xj ,令 xi 为 xj 的前一个迭代器,如果 xj 为 begin ,则 xi置为 end。情况分析如下:
- 如果 xj 为 end,代表所有的区间起点都比 val 小,看 xi 对应的 start 和 end,starti 肯定更小.
- 此时看 endi 和 val 的关系,更小则形成新区间 (val,val) ,相邻则加入 xi 区间,包含则不动
- 如果 xj 为 begin,代表所有的区间起点都比 val 大,当且仅当此时 xi 为end
- 若startj - 1 = val,右区间左侧扩大,否则形成新区间val,val
- 其他情况下,考虑 xi 和 xj,此时有:starti <= (endi ? val 这两个不确定 ) < startj <= endj
- 如果endi + 1 = val = startj - 1,xi 和 xj 区间合并形成大区间
- 如果endi + 1 = val,左区间右侧扩大
- 如果startj - 1 = val,右区间左侧扩大
- 如果val <= endi,不变
- 如果都不是,形成新区间 (val,val)
10/10 排列硬币
难度 EASY
用了数学的方法,假设最后一行满,则有 $(1 + x) *x/2=n$ ,解得$x = \sqrt{2n+0.25}-0.5$,由于最终答案是整数,即使最后一行不满,即n减小(但至少有一个),结果在取整后也不会变。
10/11 整数转换英文表示
难度 HARD
这是个勾八的hard,题目毫无质量可言,写了一堆switch,每三个数一处理就行。
10/12 两数相除
难度 MEDI
不能用乘除,但是可以用移位,把被除数拆成除数的若干个2的次幂之和加上余数,这个2的次幂之和就是商。由于整数限制了范围,这个算法不算很慢
10/13 Fizz Buzz
难度 EASY
不是很懂,if-else就行。
10/14 山峰数组的顶部
难度 EASY
这个月好多简单题。山峰数组用二分查找就行,之前貌似做过(也可能是黄宇老师的课上)。
10/15 外观数列
难度 MEDI
模拟一下,数组迭代处理即可,没想到更好的办法,一看官方题解居然有打表,乐。
10/16 给表达式添加运算符
难度 HARD
好难好难好难。看了题解勉勉强强复现一下,这题用到了回溯法,这个方法老师没说过且用的不太多,每次遇到都得想半天。通过这题学到了c++的函数包装器function。由于具体做法完全参考题解,直接看题解就行。
私以为回溯法用来回溯的函数,类似记忆化的dfs,在搜索完某种情况后记得恢复状态,比如这题for循环结束后的str.resize(loc);
。
10/17 二叉搜索树中第K小的元素
难度 MEDI
bfs是一种很特殊的树,可以先预处理树得到每个节点的所有后代(包括自己)的数量,然后再进行循环:
- 左子树所有节点数量为 left,右子树为 right,那么
- 如果 left<k - 1,更小的节点在右子树中,k 减少left + 1,移动到右子
- 如果left == k - 1,那么本节点就是第k小的节点
- 否则,更小的节点在左子树中,移动到左子
10/18 数字的补数
难度 EASY
数学题,通过不断右移1,2,4,8,16并与自己“或”,得到数tmp,其最高的1与原数num位置相同,该位往后全为1。将num符号位置1后取反,得到有前导1的补数(但符号位为0而不是负数),再和tmp做与操作将前导1置0即可。
10/19 添加与搜索单词 - 数据结构设计
难度 MEDI
用字典树存储所有单词。普通单词的查询较为容易,含’.’的单词,遇到’.’时需要从当前位置的所有可能分叉(当前节点的26个子节点中所有有效的,即该位置可以为任意字母)递归查询’. ‘之后的单词,但凡有一种查到就返回true。
10/20 最小操作次数使数组元素相等
难度 EASY
这题并没有求最终数组的结果而只需要考虑次数。
在最优的操作情况下,每次是让最大的数不动,其他数均+1,其实等价于最大的数 - 1,这个trick挺巧妙的。
10/21 加一
难度 EASY
铸币简单题,找到最右侧的非9数字就行,只有找不到的情况下需要建新数组(例如99999这样的数+1)。
10/22 求众数 II
难度 MEDI
我只知道求超过n/2的众数可以选举法,这题没想到空间复杂度O(1)的算法(不限制复杂度直接哈希表就行),看了题解原来这种也可以用选举法,看来只要是限制了超过多少多少的众数都可以,最终结果可能有多个而已。
10/23 构造矩形
难度 EASY
又是简单题,开方后一个一个找就行。
10/24 大礼包
难度 MEDI
记忆化的dfs,过程比较繁琐但不算难
10/25 搜索二维矩阵 II
难度 MEDI
一开始没考虑清楚,想的是先横向二分查找确定一列,纵向二分查找确定一行,误以为只会出现在这些位置,而这明显不对。正确的想法是,设当前坐标元素(x, y)值为z,那么
- 如果z == target,找到目标
- 如果z < target,那么意味着这一行在 z 左侧的都没有可能
- 如果z > target,那么意味着这一列在 z 下侧的都没有可能
那么,如果我们从右上角出发,即右侧、上侧无有效元素,每次在2、3两种情况下就可以筛除整行或整列,并且依然保持z在右上角。因此从右上角向左下角搜索,搜到超出边界则失败。
10/26 下一个更大元素 I
难度 EASY
这道题最好的思想是单调栈:
一但要求下一个更大的元素,就是用单调栈解
我当时没想到,用的想法类似单调栈,即用一个哈希表记录某个元素的下一个最大元素是多少,初始化最右侧为-1,从右向左遍历:
for(int i = n - 2; i >= 0; i--){
int cmp = nums2[i + 1];
while(cmp < nums2[i] && cmp != -1){
cmp = bigger[cmp];
}
bigger[nums2[i]] = cmp;
}
cmp 初值为 i+1 元素的值,如果 cmp 比 i 元素小,找cmp元素记录的下一个最大元素,不断循环直到找到比 i 大的或是递归到 -1(即没有更大的),把这个值赋给 i 位置。用均摊分析法分析,虽然是双重循环,但时间复杂度依然在 O(n) 。
其实这个思路和单调栈基本一致,不过平均来说单调栈用的空间更小。
10/27 删除无效的括号
难度 HARD
本题题解中提供了其他较好的方法(比如回溯),有时间值得一看。
我用的方法基于BFS,基本类似官方题解的第二个解法。既然要删除最少的括号使之有效,那么每次从当前队列中拿出一个元素,删除任意一个括号加入队列,将其弹出,那么在一次循环开始时(或结束时)检查发现有效字符串,队列里的都是删除等量括号的,即包含所有可能的结果,找出所有有效的即可。
在这个基础上需要进行优化。首先,删除的顺序不同可能导致产生相同的中间结果,所以要用哈希表代替队列来避免。其次,删除任意一个括号的做法会在一些情况下带来大量冗余。由于我们可以在一开始通过简单的遍历得出需要删除多少个左、右括号才能使之有效,我们可以把这两个数字记录下来,作为unordered_map
的 key,删除左括号时先检查是否能删除,右括号同理。避免了对于类似 “((((((((((())))”这样明显只需要删除左括号的进行了删除右括号,算是空间换时间。
10/28 重新排序得到 2 的幂
难度 MEDI
用大小为10的字符串记录数字中0到9的出现次数,把所有范围内的2的幂对应的字符串存入哈希表,再查询即可。题解中介绍了一种回溯算法,在这题中用不上,但是思路挺通用的。
10/
难度 EASY
10/
难度 MEDI
10/
难度 HARD