1.长度最小的子数组
思路:这道题的滑动窗口方法相对于暴力求解法能把时间复杂度从(n2)提升到了O(n),关键在于利用了连续子数组这个性质,暴力求解法的时间复杂度为0(n^2)是因为其先移动头指针,对于每一个头指针都要从头指针的位置开始向后遍历重新找到使得数组长度更小的尾指针。而滑动窗口方法就是利用了子数组中的元素是连续的这一性质,先移动尾指针,再移动头指针,让效率更高了。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int i=0,j;
int sum=0,length=0,min_length=INT32_MAX;
for(j=0;j<nums.size();j++){
sum+=nums[j];
while(sum>=target){
length=(j-i+1);
min_length=length<min_length ? length:min_length;
sum-=nums[i++];
}
}
return min_length==INT32_MAX?0:min_length;
}
};
2.水果成篮
map(由关键字-值对):unordered map (关键字唯一) unordered mutimap (关键字可重复)
unorder_map中的find函数是查找key所对应的value的位置(迭代器)。
若存在,则返回key所对应的value的迭代器,通过it->fisrt和it->second来获取和操作键和值,能够直接操作map中的键值对。
所不存在,则返回unorder_map::end。
思路:这道题和最小长度字符串其实是一样的思路,同样也利用了寻找的序列一定连续这一性质,因此也可以通过j-i+1来直接计算最大子序列长度,不同之处在于要借助map这一数据结构来统计每种元素的数量。
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int max_count=0;
int count=0;
int i=0,j=0;
unordered_map<int, int> cnt;
for(;j<fruits.size();j++){
cnt[fruits[j]]++;
while(cnt.size()>2){ //当元素种类大于2,就开始缩小左边界
auto it=cnt.find(fruits[i]); //通过在map中查找左边界的元素,并将其计数值递减,
it->second--;
if(it->second==0) cnt.erase(it); //当计数值递减为0,就删除这个键值对
i++; //同时计数值每递减1,也意味着左边界也要右移一位
}
max_count=(j-i+1)>max_count ? (j-i+1):max_count; //因为找到子序列元素是连续排列的,因此他们的长度就可以用j-i+1来求
}
return max_count;
}
};
3.最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
思路:
怎么判断s中的子串包含了t中的全部字符:创建distance变量,存储s中的子串中的t中字符的个数,当distance等于t字符串的长度,就表示子串包含了t中全部字符。这样用O(1)的方法就完成了判断。
特别要注意的是distance的增加和减少条件如下图,这样能保证通过distance的值就能判断子串是否包含了t中全部字符,而win_s哈希表还是
class Solution {
public:
string minWindow(string s, string t) {
unordered_map <char,int> win_s,total_t;
int tlen=t.size();
int slen=s.size();
int left=0,right=0;
int distance=0;
int minlen=slen+1;
int begin=0;
for(auto w:t){
total_t[w]++;
}
while(right<slen){
// if(total_t[s[right]]==0){
// right++;
// continue;
// }
if(win_s[s[right]]<total_t[s[right]]){
distance++;
}
win_s[s[right]]++; //distance只有满足上面条件才增加,但是right右移时win_s哈希表会为每一个字符计数。
right++;
while(distance==tlen){
if((right-left)<minlen){
minlen=right-left;
begin=left;
}
// minlen=(right-left)<minlen?(right-left):minlen;第一次写的时候没有使用上面的if语句,而是直接写了这两句,每次无条件更新begin,这会导致报错。
// begin=left;
// if(total_t[s[left]]==0){
// left++;
// continue;
// }
if(win_s[s[left]]==total_t[s[left]]){
distance--;
}
win_s[s[left]]--;
left++;
}
}
if(minlen==slen+1) return "";
else return s.substr(begin,minlen);
}
};
4.螺旋数组
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
int starx=0,stary=0;
int offset=1,count=1;
int i,j;
int mid=n/2;
vector<vector<int>> res(n,vector<int>(n,0));
for(int round=0;round<(n/2);round++){
i=starx; //这里的i和j的分别赋值一定不能少,因为当上一轮循环结束,i和j并没有在下一圈循环的起点。
j=stary;
for(j=stary;j<(n-offset);j++) //这里是采用分别打印四条边的方法,边界采用左闭右开,
res[i][j]=count++;
for(i=starx;i<(n-offset);i++)
res[i][j]=count++;
for(;j>stary;j--)
res[i][j]=count++;
for(;i>starx;i--)
res[i][j]=count++;
starx++;
stary++;
offset++;
}
if(n%2==1) res[mid][mid]=count; //这里的Mid也要单独计算,原因和什么i,j每一轮都要赋值一样,因为当N是奇数时,中间的这个点并不能被计算到
return res;
}
};
5.螺旋数组
思路:这道题和上面的螺旋数组的区别是上面的数组一定为正方形,长和宽都为n,而本题长宽不一定一致,因此要先求出矩阵的长宽。
上题的方法可以结合事例2的矩阵进行理解,第一圈第一条边,i不变,j的遍历范围是【0,2】,遍历第二条边时j不变,i的遍历范围是【0,1】,后面两条边类似,当进入第二次循环,即开始遍历更内一层,第一条边j的遍历范围是【1,1】,第二条边i的遍历范围是【1,1),因此没有可取的值,后面两条边同理,这里就可以看出规律,每进到更内一层,遍历边的起点+1,而终点-1。另外通过观察可以发现matrix[1][2]这个点遍历不到,因此可以总结出规律,即当矩阵的长或宽为奇数的时候,matrix[length/2][width/2]这一位置遍历不到,需要单独处理,这与上一题的matrix[n/2][n/2]类似。
要采用上题的方法,还必须要计算出遍历矩阵所需的圈数,可以发现圈数就是max(length/2,width/2),即矩阵长除2和宽除2这两个数中的最大值。
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
int length=matrix.size();
int width=matrix[0].size();
vector<int> nums(length*width,0);
int starx=0,stary=0;
int i,j,vector_num=0;
int offset=1;
int round_num=(length/2>width/2)?(length/2):(width/2);
int length_mid=length/2,width_mid=width/2;
for(int round=0;round<round_num;round++){
i=starx; //这里的i和j的分别赋值一定不能少,因为当上一轮循环结束,i和j并没有在下一圈循环的起点。
j=stary;
for(j=stary;j<(width-offset);j++) //这里是采用分别打印四条边的方法,边界采用左闭右开,
nums[vector_num++]=matrix[i][j];
for(i=starx;i<(length-offset);i++)
nums[vector_num++]=matrix[i][j];
for(;j>stary;j--)
nums[vector_num++]=matrix[i][j];
for(;i>starx;i--)
nums[vector_num++]=matrix[i][j];
starx++;
stary++;
offset++;
}
if(length%2==1||width%2==1) nums[vector_num]=matrix[width_mid][length_mid]; //这里的Mid也要单独计算,原因和什么i,j每一轮都要赋值一样,因为当N是奇数时,中间的这个点并不能被计算到
return nums;
}
};
=================================================================
刚开始完全仿照方形矩阵,写出上面的写法,对比下面的答案,可以发现,当矩阵为只有一行或一列时,无法用上面的程序进行处理,如下图所示,因此需要进行单独分类处理.
另外需要注意的是,下面的答案每添加一个元素,都要判断是否已经添加完所有数据,这样做得原因是可以参考上面的事例2,当遍历到第二圈,内部其实是一个只有一行的矩阵,而下面的程序遍历只有一行的矩阵时,是会重复遍历其中的元素的,不信带数进去可以试试,保证数据不被重复计入结果数组的方法就是在每次添加元素后进行判断,因此要进行该判断。
在开始没有使用push_back方法时,报错矩阵数组下表越界,
21ERROR: AddressSanitizer: heap-buffer-overflow on address 0x604000000180 at pc 0x00000034d048 bp 0x7ffc14ff7430 sp 0x7ffc14ff7428
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int> ans;
int n=matrix[0].size();
int m=matrix.size();
if(m==1) return matrix[0];
if(n==1){
for(int i=0;i<m;i++)
ans.push_back(matrix[i][0]);
return ans;
}
int startx=0, starty=0;
int loop=max(n, m)/2;
int del=1;
int i,j;
while(loop--){
for(j=starty;j<starty+n-del;j++){
ans.push_back(matrix[startx][j]);
//由于可能不是方块,所以每次循环都需要确定是否已经添加完所有数据,避免重复添加
//示例如[[2,3,4],[5,6,7],[8,9,10],[11,12,13],[14,15,16]]
if(ans.size()==m*n) return ans;
}
for(i=startx;i<startx+m-del;i++){
ans.push_back(matrix[i][j]);
if(ans.size()==m*n) return ans;
}
for(;j>starty;j--){
ans.push_back(matrix[i][j]);
if(ans.size()==m*n) return ans;
}
for(;i>startx;i--){
ans.push_back(matrix[i][j]);
if(ans.size()==m*n) return ans;
}
startx++;
starty++;
del+=2;
}
if(n%2 && m%2)
ans.push_back(matrix[n/2][m/2]);
return ans;
}
};
最终根据上一题代码风格更改的代码如下:
这一道题相比上一道:
1.多出了处理只有一行或一列的矩阵的部分。而上一道题的矩阵是方阵,不会出现这种情况。
2.处理非一行(列)的矩阵的部分,因为矩阵不一定是方阵,因此添加元素时要增加判断语句
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
int length=matrix.size();
int width=matrix[0].size();
vector<int> nums;
int starx=0,stary=0;
int i,j,vector_num=0;
int offset=1;
int round_num=(length/2>width/2)?(length/2):(width/2);
int length_mid=length/2,width_mid=width/2;
if(length==1){ //处理只有一行或一列的矩阵的部分
for(j=0;j<width;j++) nums.push_back(matrix[i][j]);
return nums;
}
if(width==1){
for(i=0;i<length;i++) nums.push_back(matrix[i][j]);
return nums;
}
for(int round=0;round<round_num;round++){ //处理非一行(列)的矩阵的部分,因为矩阵不一定是方阵,因此添加元素时要增加判断语句
i=starx; //这里的i和j的分别赋值一定不能少,因为当上一轮循环结束,i和j并没有在下一圈循环的起点。
j=stary;
for(j=stary;j<(width-offset);j++) {//这里是采用分别打印四条边的方法,边界采用左闭右开,
nums.push_back(matrix[i][j]);
if(nums.size()==length*width) return nums;}
for(i=starx;i<(length-offset);i++) {
nums.push_back(matrix[i][j]);
if(nums.size()==length*width) return nums;}
for(;j>stary;j--) {
nums.push_back(matrix[i][j]);
if(nums.size()==length*width) return nums;}
for(;i>starx;i--) {
nums.push_back(matrix[i][j]);
if(nums.size()==length*width) return nums;}
starx++;
stary++;
offset++;
}
if(length%2==1||width%2==1) nums.push_back(matrix[length_mid][width_mid]);这里的Mid也要单独计算,原因和什么i,j每一轮都要赋值一样,因为当N是奇数时,中间的这个点并不能被计算到
return nums;
}
};