文章目录
前言
本周完成的题目有:
- 1 两数之和(简单难度)
- 892 三维形体的表面积(简单难度,每日一题)
- 914 卡牌分组(简单难度,每日一题)
- 999 可以被一步捕获的棋子数(简单难度,每日一题)
- 820 单词的压缩编码(中等难度,每日一题)
- 1162 地图分析(中等难度,每日一题)
- 2 两数相加(中等难度)
- 3 无重复字符的最长子串(中等难度)
博客专栏地址:https://blog.csdn.net/feng964497595/category_9848847.html
github地址:https://github.com/mufeng964497595/leetcode
1 两数之和
题目描述
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
思路解析
- 一般题目拿到手,先考虑最简单的暴力算法。题目要找数,而且只找两个,那就很简单地用两个for循环嵌套遍历一波,把两两组合都算出来就可以了,注意别重复取同一个数了,例如输入[1,2,3],要找4,就不能返回[1, 1],而要返回[0, 2]。
- 暴力算法之外,有没有更快一点的?当然有啦。题目输入是一个数组,要我们输出下标,那一般就是要把数值的值和下标记录起来。所以,我们可以用一个map来记录值与下标的关系,把数组的值和下标都放到map里,然后遍历map,看目标值-当前遍历到的值是不是也在map中,且与当前遍历到的值不在同一个位置,如果是的话,那就找到结果了。
- 方法2有没有问题?当然有=_=||。考虑这样一组用例:[1,3,3,2],目标值是6,那么在预处理的时候,map对于key相同的值会用后插入的结果来覆盖掉,所以map里面其实只有一个3,那么就会error啦。那可能会想到,使用multimap是不是能解决?答案是可以的,不过代码写起来会有点恶心,因为multimap查找key只会返回找到的其中一个值,没办法把所有key相同的结果都返回回来,要自己去搜,这性能就有点影响了。
- 那要怎么解决呢?其实很好办。我们前面是先预处理数组里的值到map中,才会有这个冲突的问题,那如果我们不预处理,而是先把map置为空,然后遍历数组,看目标值-当前遍历到的值是否在map中,如果不在,就插入map,在的话,那就是找到结果了,而且也不存在找到的两个值是同一个下标的情况。这样就解决上面的冲突问题啦。
- 可能有同学会问,对于key相同的情况,在map上不是依旧会冲突吗?其实不然。按题目的描述,一次只会有一个答案,那么对于key相同且其中一个下标是答案的一部分的情况,就只会是key相同的两个下标都是答案,且只会有两个key相同的下标,否则就跟题目的描述冲突了。既然只会有这种情况,而我是边遍历数组边插入map,那么就不存在这两个key都在map中这种情况了。
示例代码
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
// 用map来记录已搜索过的值&对应的下标
std::map<int, int> mVal2Idx;
vector<int> vRes;
// 提前分配空间,避免push_back触发空间重新分配,影响性能
vRes.reserve(2);
int size = nums.size();
for (int i= 0; i < size; ++i) {
int val = target - nums[i];
auto mit = mVal2Idx.find(val);
if (mVal2Idx.end() == mit) {
// 没找到,将数组当前值送入map
mVal2Idx.insert(std::make_pair(nums[i], i));
} else {
// 从map中找到另一个值,那就得到结果了。
// 对于数组中有两个值相同的情况也兼容了,
// 因为题目说只有一个答案,那么如果出现两个值相同且其中一个是答案的情况,
// 就只能是这两个值都是答案这一种可能。
// 而此时map里只有一个值,所以不存在下标被覆盖的情况
// mit->second比较小,放前面
vRes.push_back(mit->second);
vRes.push_back(i);
break;
}
}
return vRes;
}
};
892 三维形体的表面积
题目描述
在 N * N 的网格上,我们放置一些 1 * 1 * 1 的立方体。
每个值 v = grid[i][j] 表示 v 个正方体叠放在对应单元格 (i, j) 上。
请你返回最终形体的表面积。
思路解析
- 算表面积,暴力算法就不太能搞了,要换换思路当成是智力题
- 假设,只有一列111的正方体,共n个,那么表面积怎么算呢?发挥下想象力,n个正方体竖着叠在一起,那么有四个侧面,每个侧面有n个11的面,那么侧面表面积就有4n,再加上上下两个面,所以表面积是4*n+2。(这里说的n>0哈)
- 如果每列正方体都没叠在一起,共m列,那么总的表面积也就可以算了,把每一列都套入公式算一下就好了。
- 但是题目这样出了,就肯定有一些叠在一起的,那么我们就用总的表面积来减去叠在一起的表面积就好了。那要怎么算那些叠在一起的呢?
- 单独看一列正方体,它被别人挡住的情况就只会出现在上下左右这四个方向,每个方向挡掉的表面积就是看谁矮了。例如一列有4个正方体,另一列有3个,两列并排放,那对于我这一列,就是挡掉3个嘛。
- 那么思路就清晰了:遍历网格的每一列,计算4*grid[i][j]+2得到这一列没被挡的总表面积(grid[i][j]=0就不用算了,直接跳过)。然后在分别比较一下grid[i][j]与它相邻的四个方向,减去小的那个高度,就得到当前列最终的表面积了。每一列都这样算就ok了。
- 是不是可以再优化一下呢?想想看哈,例如我们从i=0~n-1,j=0-n-1来遍历每一个网格,在访问它的四个方向的时候,对于当前列的左、下两个方向要减去的表面积,其实已经在计算左边这一列和下面这一列的时候就计算出来了,没必要再计算一次(两列并在一起,他们互相挡掉的表面积是一样的),在减去的时候*2就可以不用再浪费时间去再算一次左、下两个方向了。这可省掉了一半的时间。