伴随着互联网裁员的压力和入职时间的临近,又开始刷每日一题啦
本次只记录每题的要点,不关注具体实现
时间为倒序
4-26 Lc883 三维形体投影面积
- 俯视图就是判断是否大于0,每个柱子的面积,要么1,要么0
- 两个侧视图分别是用i和j来遍历数组,面积是分别i和j的最大值
- 比较简单
4-25 Lc398 随机数索引
-
这题还是挺有意思的,要返回数组中目标随机数的任意随机一个的索引
-
如果输入数组不大,可以放到内存中来,而且需要查询的次数较多,那么使用map来存储比较好,这样类生成的时间是O(n),而后面查询的话是O(1)
-
如果输入的数组比较大,无法放进内存,或者查询的次数较少,那么可以用水塘抽样法,我也是看了题解才知道的
- 这个思想在于,当找到第一个target时,我们在 [ 0 , 1 ) [0,1) [0,1)之间随机,这个是一定的,然后每找到一个target,我们就用当前的 1 / k 1/k 1/k概率来替代结果,所以分别是 1 , 1 / 2 , 1 / 3 , 1 / 4...1 / k 1,1/2,1/3,1/4...1/k 1,1/2,1/3,1/4...1/k,所以任意一个target索引被选中的概率就是 p = 1 / i ∗ i / i + 1 + . . . k / k + 1 = 1 / k p=1/i * i/i+1 +... k/k+1=1/k p=1/i∗i/i+1+...k/k+1=1/k,最终结果就是 1 / k 1/k 1/k
- 这里需要注意,第i个被选中的概率,是不与前面的target相关的,值与后面的影响
- 这个有点神奇,数学的魅力嘛,反正记下来,没见过基本就不会,谁能想到证明这个…
-
除此之外,水塘抽样,可以解决O(n)内,O(1)空间的目标随机抽样问题,该题确实有点意思
4-24 Lc868 二进制间距
- 没啥意思,不赘述
4-23 Lc587 安装栅栏
- 凸包问题
- Graham方法
- 核心点在于,寻找第一个凸点,然后按照角度来遍历, 每两条边的凸出方向一定是向外的,这个通过两条边向量的角度来判断,每次两条边来找凸,从而最终找出凸包边界
4-22 Lc396 旋转函数
- 说实话做完这题,我还是没弄懂它这个旋转是啥意思,形容有点莫名其妙
- 观察示例,其实可以发现f(0)和f(1)之间是有雪薇的差距的,除了nums中的某个数,其他所有数都是增加了一个,而那个数,则是减少了n个
- 发现了这个规律,就可以O(n)解出这题了
- 代码也很好写了
class Solution {
public int maxRotateFunction(int[] nums) {
int n = nums.length, sum = 0, f = 0;
for(int i=0;i<nums.length;i++){
sum += nums[i];
f += i*nums[i];
}
int res = f;
for(int i=nums.length-1;i>0;i--){
f += sum - n*nums[i];
res = Math.max(res,f);
}
return res;
}
}
4-21 Lcxxx
- 这题太简单,没必要赘述
4-20 Lc388 文件的最长绝对路径
- 这题最坑的地方在于输入中\n是换行符,而不是字符,而\t也是一样,因此不能当做字符来对待它们,这也导致我花了很长时间弄这个
- 输入已经是dfs的顺序了,所以需要记录上一层的长度,
- 使用栈来进行模拟,栈中元素为 [ 当 前 层 数 , 前 一 层 长 度 , 当 前 目 录 长 度 ] [当前层数,前一层长度,当前目录长度] [当前层数,前一层长度,当前目录长度]
- 出栈条件:当前层数<栈顶层数
- 入栈条件:当前层数>=栈顶层数,当前不是文件而是文件夹
- 然后需要注意对文件进行判断,通过 . . .进行判断
class Solution {
//首先通过\n来把不同的名称分开
//建立栈,栈中元素为[x,x,x],分别为层数,上一层长度,本名称长度
//遍历名称,如果当前的层数比栈顶大于等于,则将当前入栈
//如果栈顶层数小于等于自己,则弹出为止
//如果当前的名称为文件,那么不可能有下一层,那么就不如栈,即栈里只放文件夹,不放文件
public int lengthLongestPath(String input) {
Stack<int[]> stack = new Stack<>();
String[] ss = input.split("\n");
int len = 0;
for(int i=0;i<ss.length;i++){
int[] curInfo = parse(ss[i]);
while(!stack.isEmpty()&&curInfo[0]<stack.peek()[0]) stack.pop();
int preLen = stack.isEmpty()?0:stack.peek()[1];
preLen += stack.isEmpty()||stack.peek()[0]==curInfo[0]?0:stack.peek()[2];
//相同层次时,就不用相加了
if(curInfo[2]==1) len = Math.max(curInfo[1]+preLen,len); // 文件
else stack.push(new int[]{curInfo[0],preLen,curInfo[1]});//目录
}
return len;
}
private int[] parse(String s){
int level = s.lastIndexOf("\t")+1;//层数
int isFile = s.contains(".")?1:0;//判断是否是文件
int len = s.length() - level + (level>0?1:0);//补齐/的字符长度
return new int[]{level,len,isFile};
}
}
4-19 Lc821 字符的最短距离
- 求字符串中每个字符到目标字符的最短距离,字符串中可能含有多个目标字符
- 可以O(n)求解
- 正向一遍,找到每个字符右边的最近目标字符
- 反向一遍,找到每个字符左边的最近目标字符
- 取最小值,即可完成
4-18 Lc386 字典序排数
- 对 1 − n 1-n 1−n进行字典序排序
- 思路是通过dfs来生成,模拟字典序
- 逻辑为:
dfs(num):
如果(num<=n),添加
dfs(num*10)
如果num+1不产生进位,dfs(num+1)
- 核心在于+1不能产生进位,否则会与*10的结果重复
4-17 Lc819 最常见的单词
- 核心在于,如何对一段英文进行分词
String[] ss = paragraph.toLowerCase().split("[ !?',;.]");
- 当然,这会产生空格字符串,所以在后面就直接continue"" 即可
4-16 Lc479 最大回文数乘积
- 本题是将范围内的回文数分解成两个数的乘积,要求最大
- 思路是从大到小,挨个生成回文数,然后看能不能分解
- 核心在于回文数的生成,整体逻辑也需要注意
回文数: 123321 轴对称
那么实际可以由 123生成,p(123) = 123*1000 + rev(123) = 123 321
其中rev为翻转,reverse
那么该比该回文数小的下一个回文数是 122 221 = 122*1000 + rev(122) = p(122)
即只需要对123进行p()操作即可
因此我们从一开始就可以用99XX来生成回文数
至于分解,就从9X开始,一个个除,能除尽为止,一旦因子的平方也小于回文数时,就剪枝返回
4-15 Lc385 迷你语法分析器
- 这题我用的递归做的,挺麻烦,用栈会相对简单一点
- 没啥好说的,挺经典的题,但是题目要求使用的数据结构真是让我想吐芬芳,你大爷,比宫崎英高都恶心
4-14 Lc1672 最富有客户的总资产
- 太简单了,力扣要亡
4-13 Lc380 O(1)时间插入 删除 和获取
- 需要设计数据结构set来保证O(1)时间xxx
- 难点在于等概率随机获取,一般想到实现就是在 [ 1 , n ] [1,n] [1,n]之间生成随机数,通过索引来获取,此时就可以是O(1)的
- 核心在于,通过一个map和数组实现,删除的实现尤为关键,需要保证索引的连续
map中存放 num-index
数组中存放num
二者相对应
当插入时,同时更新map和数组
当获取随机时,生成数组容量的随机数,返回
当删除时,将数组的最后一个数与被删除的数交换,保证索引的连续,然后更新map
- 本题的进阶为381应该是,随机的概率是线性的,可以重复插入,这个也很简单
- 直接把map中 num-index换成 num-indexs:List 即可,当可以重复插入时,本身随机就是线性的
4-12 Lc806 写字符串需要的行数
- 这题也简单,就是整个词不能跨行,所以就用剩余的空间与当前词比较,不行的话就将剩余空间重置为一整行
- 感觉有点像是内存空间放对象一样
4-11 Lc357 统计各位数字都不同的数字个数
- 这题还是不错的
- 首先对于n=5的情况,当xxxxx中第一个x为0时,其实这就是f(4)的结果,所以这有个递归的过程
- 而对于第一个x不为0时,此时有多少种可能,这就成了高中学过的概率问题,第一个x有1-9 9种可能,第二个x可以为0,它与第一个不同,所以也有9种可能,第三个x有8种可能,第四个7…
- 实现起来很简单,想也不是那么难想,但确实有趣,可以扩展思路
4-10 Lc804 唯一摩尔斯密码词
- 纯属浪费时间
4-9 Lc780 到达终点
- 核心在于,从起点到终点,每一步都会有两个选择,而从终点到起点,实际上每次只有一个选择,所以倒着来是本题的关键
- 从终点出发,每次通过横纵坐标的减法减小
- 会出现,横纵坐标差距过大,导致时间复杂度太大的问题,所以做减法时,需要尽可能的多减一点,但是又不能减到结果小于起点,所以这里得稍微麻烦一点
先记到这,这个月是月初开始写的,写到今天也有半个月了,全部记完也没啥意义