g3云网站seo标题关键词优化
11. 239 滑动窗口最大值
经典单调栈问题。一个deque维护单调栈,front都是最大值,递减存储,因此最大值就是front。但问题就是有个窗口大小,更需要更新最大值。k窗口往右移动,弹出左边的值。因此直接给deque中存储索引i,如果i-front>=k,说明front不在k窗口范围内,需要弹出。
class Solution {
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {vector<int> result;deque<int> q; //优先队列for(int i=0; i<nums.size(); i++){while(!q.empty() && nums[i]>=nums[q.back()]) //保持单调性,左往右是递减的q.pop_back();q.push_back(i);if(i - q.front()>=k) //保证队列右边值在窗口范围内q.pop_front();if(i+1>=k) //符合位置就可以读结果了result.emplace_back(nums[q.front()]);}return result;}
};
12. 76 最小覆盖字串
滑动窗口问题。整体思路就是两个map,一个mapt记录的是目标字符串,maps表示滑动窗口得map,每次对比两个map就好。滑动窗口时,先更新窗口右边界,左边界默认为0,不断更新右边,直到窗口满足条件。
- 窗口满足要求就每次更新结果,结果left和len。就可以得到字符串
- 满足条件基础上开始尝试缩小窗口,因为题目要求最小,因此移动左边界,每次要判断最左边元素是不是在目标mapt中,如果在,需要改变maps,因为会涉及移动窗口是否满足条件。如果不再,那就不用管,说明可以缩小的。
class Solution {
public:unordered_map<char, int> s_map, t_map;bool check(){for(const auto& m:t_map){if(s_map[m.first] < m.second)return false;}return true;}string minWindow(string s, string t) {for(const auto& m:t)t_map[m]++;int l=0, r=-1;int len = INT_MAX, anl=-1, anr = -1;cout<<s.size()<<endl;cout<<int(s.size())<<endl;while(r<int(s.size())){if(t_map.find(s[++r]) != t_map.end()) //滑动右边边界s_map[s[r]]++;while(check() && l<=r){ //满足条件的字符串,开始滑动左边边界if(r-l+1 < len){ //长度更小,更新即可len = r-l+1;anl = l;}if(t_map.find(s[l]) != t_map.end()) //左边字母在目标t中s_map[s[l]]--; //s—map弹出l++; //如果上一步左边字母不在t中,smap不需要改变,l++即可。}}return anl==-1 ? string() : s.substr(anl, len);}
};
13. 53 最大子数组和
解法一:遍历求和,因为要求得最大子数组和,因此如果目前和已经是负数,那就说最终的子数组一定不会有前面的,因为负数会影响后面的子数组之和。因此,需要每次遍历加到和为负。那就清空为0,重新开始求和,就表示更新了子数组的左边界。
class Solution {
public:int maxSubArray(vector<int>& nums) {int result = INT_MIN;int add = 0;for(int i=0; i<nums.size(); i++){add += nums[i];if(add>result)result = add;if(add<0) //目前子数组和为负,更新左边界。add = 0;}return result;}
};
解法二,动态规划,每次求和更新dp,dp就在于是求和之后的大,还是本身num大,其实本质也是看之前和是否为负数。
class Solution {
public:int maxSubArray(vector<int>& nums) {int result = nums[0];int add;for(int i=0; i<nums.size(); i++){add = max(add+nums[i], nums[i]);result = max(add, result);}return result;}
};
14. 56 合并区间
贪心问题,每次都取最优,先sort排序,就按照左边界从小到大排序了,因此结果得第一个区间左边界可以确定,就在于右边界。合并就是看区间有没有重叠,也就是第一个区间得右边界和第二个区间的左边界。现在结果已经放了第一个区间,那就从第二个区间(i=1)开始遍历,看看i区间的左边界和结果中back(最右边得区间)右边界大小,如果大于,那就说明没重叠,结果back不需要更新,可以确定,将i区间push即可。如果小于,说明有重叠,那么合并之后的右边界取决于,第两个右边界谁大!思路就清晰了。
class Solution {
public:vector<vector<int>> merge(vector<vector<int>>& intervals) {sort(intervals.begin(), intervals.end());vector<vector<int>> result;result.emplace_back(intervals[0]);for(int i=1; i<intervals.size(); i++){if(intervals[i][0] <= result.back()[1])result.back()[1] = max(result.back()[1], intervals[i][1]);else{result.emplace_back(intervals[i]);}}return result;}
};
15. 238 除自身以外数组的乘积
典型数组前缀和问题,乘积包括两部分,前面乘积和后面乘积,例如,第i处的结果就是i-1之前的乘积和i+1之后的乘积(闭区间),因此求两个数组,一个前缀乘积,一个后缀乘积,就可以得到所有结果
class Solution {
public:vector<int> productExceptSelf(vector<int>& nums) {int len = nums.size();vector<int> Lnum(len), Rnum(len);vector<int> result(len);Lnum[0] = nums[0];for(int i=1;i<len-1; i++){Lnum[i] = Lnum[i-1]*nums[i]; //前缀乘积}Rnum[len-1] = nums[len-1];for(int i=len-2; i>=0; i--)Rnum[i] = Rnum[i+1]*nums[i]; //后缀乘积for(int i=1;i<len-1;i++){result[i] = Rnum[i+1] * Lnum[i-1]; //求结果}result[0] = Rnum[1]; //两个边界值需要特殊考虑result[len-1] = Lnum[len-2];return result;}
};
16. 41 缺失的第一个数
这题真抽象,非得n时间复杂度和1空间复杂度,第一次做的时候没考虑空间复杂度,零申请了n大小用于存储哈希,思路很简单,就是查每个数据的+1是否存在,不存在,那么i+1就是结果(缺失得第一个正数)
class Solution {
public:int firstMissingPositive(vector<int>& nums) {map<int,int> ma;int result = 0;for(const auto& n :nums){if(n>0 && ma.find(n)==ma.end())ma.insert(make_pair(n,1));}if(ma.find(1) == ma.end()){result = 1;return result;}for(const auto& m : ma){if(ma.find((m.first)+1) == ma.end()){ //找每个数的+1是否存在result = (m.first)+1;cout<<m.first<<endl;break;}}return result;}
};
虽然能通过,但不符合题目要求的常数空间复杂度,所以需要思考进化版,其实,一定是从正数1开始找,因此从第一个正数与1对比,第二个与2对比,哪个不满足,哪个就是result。不过记得去重。但这样需要先排序 ,时间复杂度又超了
class Solution {
public:int firstMissingPositive(vector<int>& nums) {int result = 1, i=0;sort(nums.begin(), nums.end());while(i<nums.size()){ //找到第一个正数if(nums[i]<=0){i++;continue;}break;}while(i<nums.size()){if(nums[i]==result){ //对比成功result++;while(i+1<nums.size() && nums[i] == nums[i+1]){ //去重剪枝i++;}i++;continue;}return result; //返回没有对比成功的数}return result; //都对比成功,result变成+1,返回即可}
};
就这样吧,这才是正常时间和空间权衡的做法,leetcode要求的,太极限了。
17. 73 矩阵置零
这道题很简单了,如果某个为0,记录下来它的行列号,最后在遍历,如果是之前记录的行列号,那就把值变为0就好。用两个标志数组做记录,一个表示行,一个表示列
class Solution {
public:void setZeroes(vector<vector<int>>& matrix) {//vector<vector<bool>> flag(matrix.size(), vector<bool> (matrix[0].size(), false));vector<bool> row(matrix.size()), col(matrix[0].size());for(int i=0;i<matrix.size();i++)for(int j=0;j<matrix[0].size();j++)if(matrix[i][j] == 0)row[i] = col[j] = true; //是0,记录两个标记数组for(int i=0;i<matrix.size();i++)for(int j=0;j<matrix[0].size();j++){if(row[i] || col[j]){ //如果满足某个标记数组matrix[i][j] = 0;}}return;}
};
18. 54螺旋矩阵
纯纯遍历边界考验,上下左右四个边界值,按照顺时针方向遍历,每次遍历完一个方向,判断是否越界,比如。刚开始从左往右的遍历,说明上面一行遍历完了,因此上界就要往下移动一格,比如从0到1.如果移动后的拆超过了下界,就说明遍历完了。break即可
class Solution {
public:vector<int> spiralOrder(vector<vector<int>>& matrix) {vector<int> result;if(matrix.empty())return result;int u=0, d=matrix.size()-1, l=0, r=matrix[0].size()-1;while(true){for(int i=l;i<=r;i++)result.push_back(matrix[u][i]);if(++u>d)break;for(int i=u;i<=d;i++)result.push_back(matrix[i][r]);if(--r<l)break;for(int i=r;i>=l;i--)result.push_back(matrix[d][i]);if(--d<u)break;for(int i=d;i>=u;i--)result.push_back(matrix[i][l]);if(++l>r)break;}return result;}
};
19. 48 旋转图像
顺时针旋转90°,两种做法。首先就是基本的映射,列过去是行,行对应到n-i-1列.这个自己手推举个例子就明白了。
第二种做法就是变换,先行对称变换,再主对角线变换。代码只写了第一个方法的,因为简单
class Solution {
public:void rotate(vector<vector<int>>& matrix) {int n = matrix.size();// C++ 这里的 = 拷贝是值拷贝,会得到一个新的数组auto matrix_new = matrix;for (int i = 0; i < n; ++i) {for (int j = 0; j < n; ++j) {matrix_new[j][n - i - 1] = matrix[i][j];}}// 这里也是值拷贝matrix = matrix_new;}
};
20. 240 搜索二维矩阵
基本遍历时间复杂度m*n,优化的话可以每一行采用二分
class Solution {
public:bool searchMatrix(vector<vector<int>>& matrix, int target) {for(const auto& row:matrix){auto it = lower_bound(row.begin(), row.end(), target); //二分查找if(it != row.end() && *it==target)return true;}return false;}
};
这样就马马虎虎,直到我看到了评论一个神仙做法
于是尝试,只能说,自古网络出大神
class Solution {
public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int i = 0, j = matrix[0].size()-1;while(true){if(matrix[i][j] == target)return true;else if(matrix[i][j]> target){ //遍历“左子树”j--;if(j<0) //越界就breakbreak;}else{ // 遍历“右子树”i++;if(i>=matrix.size()) break;}}return false;}
};