本篇重点练习数组删除相关的例题,主要介绍两种方法双指针法、滑动窗口。
分析:解1 暴力解法:从前往后遍历,遇到相等的就整体往前
后面的挪到前面 位置发生了变化
时间复杂度O(n2);空间复杂度O(1)
int removeElement(vector& nums, int val) {
int len=nums.size();
for(int i=0;i
解2 双指针法:设两个指针p1 p2,初始值都为0,如果不等,p1 p2同时后移;如果遇到相等的,p1不动,p2后移到不等的位置,然后覆盖掉相等的,p1 p2 同时后移。
最后p1就是去除val后的长度。
时间复杂度O(n);空间复杂度O(1)
int removeElement(vector& nums, int val) {
int len=nums.size();
int p1=0,p2=0;
for(p2=0;p2
分析:此题和上一题差不多,如果还是采用暴力解法肯定会超时。可以采用双指针法。
int removeDuplicates(vector& nums) {
int len=nums.size();
if(len<=1){
return len;
}
int p1=1;
int p2=1;
for(;p2
3. 移动零
分析:暴力解法:直接挪动(效率很低)
void moveZeroes(vector& nums) {
int len=nums.size();
for(int i=0;i
双指针 和第一个基本一样,区别在于此题要在末尾填0
void moveZeroes(vector& nums) {
int len=nums.size();
int cur=0;
int fin;
for(fin=0;fin
4. 比较含退格的字符串
补充string的相关函数
length获取字符串长度
s.substr(pos, n) 截取s中从pos开始(包括0)的n个字符的子串,并返回
分析:此题也可以用双指针,只是在遇到‘#’时cur指针要回退,其他的不变。
class Solution {
public:
string swap(string s) {//转换字符串即去掉#
int len = s.length();
int cur = 0;
int fin = 0;
for (; fin < len; fin++) {
if (s[fin] != '#') {
s[cur++] = s[fin];
}
else {
if (cur != 0) {
cur--;
}
}
}
s = s.substr(0, cur);//只取前cur个字符
return s;
}
bool backspaceCompare(string s, string t) {
s = swap(s);
t = swap(t);
if (s == t) {
return true;
}
else{
return false;
}
}
};
5.有序数组的平方
解1:先求平方,然后排序
class Solution {
public:
vector sortedSquares(vector& nums) {
int len=nums.size();
for(int i=0;i
解2:双指针法,设两个指针,平方后的最大值一定在两端,所以可以新设一个与原数组一样大小的数组,从大往小即从后往前求。
class Solution {
public:
vector sortedSquares(vector& nums) {
int len=nums.size();
vector re(len);
int k=len;
int p1=0;
int p2=len-1;
while(k--){
if(nums[p2]*nums[p2]>nums[p1]*nums[p1]){
re[k]=nums[p2]*nums[p2];
p2--;
}
else{
re[k]=nums[p1]*nums[p1];
p1++;
}
}
return re;
}
};
6 长度最小的子数组
分析:解1: 暴力解法 设两个循环,外层循环表示开始的位置,内层循环计算子数列的长度
时间复杂度 O(n2)
class Solution {
public:
int minSubArrayLen(int target, vector& nums) {
int len=nums.size();
int re=1000000;
int sublen=0;
int sum=0;
for(int i=0;i=target){//找到符合的子数列就更新当前的结果
re=re
解2 介绍一种新的方法 滑动窗口,其实也不能说是新方法,就是双指针的变形,本质上是一致的。
设两个指针不断调整开始位置与结束位置。
时间复杂度O(n)
class Solution {
public:
int minSubArrayLen(int target, vector& nums) {
int len=nums.size();
int be=0;//开始位置
int en=0;//结束位置
int sum=0;//子数列和
int sublen=0;//子数列长度
int re=1000000;//最终结果
for(;en=target){//找到符合要求的,即更新re,同时调整窗口的开始位置(这里一定要用循环来找,因为be后移可能还是符合的)
sublen=en-be+1;
re=re
扩张条件:curSum
收集结果的时刻:在收缩时
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int l=0;
int r=0;
int re=INT_MAX;
int curSum=0;
while(r<nums.size()){//左闭右开区间,当累加和小于target就扩张,当累加和大于等于target时就收缩
curSum+=nums[r++];
if(curSum<target){
continue;
}
while(curSum>=target){//收缩条件:curSum>=target
re=re<=r-l?re:r-l;
curSum-=nums[l++];
}
}
if(re!=INT_MAX){
return re;
}
return 0;
}
};
7. 水果成篮
分析:此题关键就是如何确定窗口的开始位置和结束位置,这里我先找可以放的两个类别,然后通过判断当前类别是否在刚才找的类别中,若在,说明可以放篮子里,en后移;若不在,说明篮子放不下了,因此be后移。然后重新找可以放的两个类别……
扩张的条件:进入窗口的水果是两类水果中的一类
收缩的条件:正好与扩张相反,当前水果不能再放入篮子了
收集结果的时机:由于是求最大值,当然是在扩张时收集结果
class Solution {
public:
int totalFruit(vector& fruits) {
int len=fruits.size();
int be=0;//开始位置,左闭右开区间[be,en)
int en=0;//结束位置
int re=0;//最终结果
int sum=0;//中间结果
int first;//篮子里第1种水果
int second;//篮子里第2种水果
for(;be0 && fruits[be-1]==fruits[be]){//优化1
continue;
}
first=fruits[be];//找篮子里可以放的第1种水果
for(int j=be+1;jsum?re:sum;
en++;
if(en==len){//en已到末尾,不可能再有大的了,//优化2
return re==0?0:re;
}
}
}
return re;
}
};
76. 最小覆盖子串
扩张:窗口中还未包含t中所有字符
收缩:与扩张相反
class Solution {
public:
string minWindow(string s, string t) {
unordered_map<char, int>tMap;//t的词频
unordered_map<char, int>window;//s的词频(仅统计t中出现的字符)
int l = 0;
int r = 0;
int k = 0;//窗口中包含t中字符的个数(>=)
int change = 0;
string re = s;
for (char c : t) {
tMap[c]++;
}
while (r < s.size()) {
if (k < tMap.size()) {//还未包含t中所有字符
char c = s[r];
++r;
if (tMap.count(c)) {
window[c]++;
if (window[c] == tMap[c]) {
++k;
}
}
}
while (k == tMap.size()) {
char d = s[l];
if ((r - l) <= re.size()) {
change = 1;
string newStr = s.substr(l,r-l);
re = newStr;
}
if (tMap.count(d)) {
window[d]--;
if (window[d] < tMap[d]) {
--k;
}
}
++l;
}
}
return change==1 ? re : "";//如果change==0,说明s中不存在子串
}
};
双指针和滑动窗口可以大大降低时间复杂度,针对数组的删除问题用双指针会比较快;滑动窗口非常巧妙,通过调整开始与结束位置,难的是如何确定窗口的开始与结束,在纸上模拟一下可能会有妙计。