数组相关总结
0、二分查找问题-左右边界
明确变量的含义、维护好循环不变量,根据循环不变量定义边界问题。
不同的循环不变量会使得代码边界问题发生改变。
小数据集的调试,很重要。
自己整理的看着最香:二分查找
1、双指针大法-对撞指针
【1147. 段式回文】
.substr(指定位置,指定长度)//复制字符串,从指定位置开始具有指定的长度.
如果没有指定长度或超出了源字符串的长度,则子字符串将延续到源字符串的结尾
- 暴力匹配;哈希思想+重新匹配解决哈希冲突;//这个方法看到再说
class Solution {
public:
int longestDecomposition(string text) {
int n = text.size();
for(int i = n-1;i>0;i--){
if(text[0]==text[i]&&text.substr(0,n-i)==text.substr(i)){//从后往前找和第一个字符匹配的且整个字符串要匹配
if(2*i==n) return 2;//正好左边==右边
else if(2*i<n) return 1;//因为i是从后往前,所以是<,画个图啥都明白了
else{
return 2+longestDecomposition(text.substr(n-i,2*i-n));//把第一个和后面第一个匹配搞定了递归剩下的
}
}
}
return 1;
}
};
【283. 移动零】
1.快慢双指针 On,O1
- 定义快慢双指针
- 快指针遍历nums,遇到不为零的数,将快指针对应的数给慢指针对应的数
- 慢指针再右移一位
- 快指针遍历一遍之后,所有不为0的数都已经到了数组的最前面
- 从慢指针开始,往后的数都赋值为0即可
for(int r = 0;r<n;r++){
if(nums[r]){//快指针遍历nums,遇到不为零的数
nums[l++]=nums[r];//将快指针对应的数给慢指针对应的数
}
}
for(int i = l;i<n;i++){
nums[i]=0;//从慢指针开始,往后的数都赋值为0即可
}
2.swap;右指针遇到非0一直往左挪; On,O1
for(int l = 0,r = 0,n=nums.size();r<n;r++){
if(nums[r]){
swap(nums[l++],nums[r]);
}
}
【88. 合并两个有序数组】
逆向排序,注意P1或者P2排完后(要考虑到P==-1)另一边还要排
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int p1=m-1,p2=n-1,i = n+m-1;
while(p1>=0||p2>=0){
if(p1==-1){//注意P1或者P2排完后(要考虑到P==-1)另一边还要排
nums1[i--] = nums2[p2--];
}
else if(p2==-1){
nums1[i--] = nums1[p1--];
}
else if(nums1[p1]>nums2[p2]){
nums1[i--] = nums1[p1--];
}
else {
nums1[i--] = nums2[p2--];
}
}
}
};
【167. 两数之和 II - 输入有序数组】
对撞指针; On,O1
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int l = 0,n = numbers.size(),r = n-1;
while(l<r){
if(numbers[l]==target-numbers[r]){return {l+1,r+1};}//+1是题目要求
else if(numbers[l]>target-numbers[r]){r--;}
else{l++;}
}
return {l+1,r+1};
}
};
【125. 验证回文串】
对撞指针; isalnum()+tolower() On,O1
class Solution {
public:
bool isPalindrome(string s) {
int l = 0,n = s.size(),r=n-1;
while(l<r){
while(l<r && !isalnum(s[l])){++l;}
while(l<r && !isalnum(s[r])){--r;}
if(tolower(s[l++])!=tolower(s[r--])){return false;}
}
return true;
}
};
【344. 反转字符串】
对撞指针; On,O1
- 辅助栈用于匹配括号啊匹配回文也可以,但更喜欢双指针
class Solution {
public:
void reverseString(vector<char>& s) {
int l = 0,n = s.size(),r = n-1;
while(l<r) {swap(s[l++],s[r--]);}
}
};
【345. 反转字符串中的元音字母】
对撞指针; On,O1/On?
class Solution {
public:
bool isAEIOU(char a){
return a=='a'||a=='e'||a=='i'||a=='o'||a=='u'||a=='A'||a=='E'||a=='I'||a=='O'||a=='U';
}
string reverseVowels(string s) {
int l = 0,n = s.size(),r = n-1;
while(l<r){
while(l<r&&!isAEIOU(s[l])) {l++;}//这里如果没有l<r就不行,有点疑惑,
//噢噢因为你是while,如果没有这个的话你下面的l++会多走一次导致错误
while(l<r&&!isAEIOU(s[r])) {r--;}
swap(s[l++],s[r--]);
}
/*
while(l<r){
if(!isAEIOU(s[l])) {l++;}//用if就不用这里再来一个l<r
if(!isAEIOU(s[r])) {r--;}
else if(isAEIOU(s[l])&&isAEIOU(s[r])) {swap(s[l++],s[r--]);}//但是用if不像while一样
//确定到这里的时候一定是isAEIOU(s[l]),所以要多一个判断
}*/
return s;
}
};
【11. 盛最多水的容器】
对撞指针;考虑好指针移动条件。
class Solution {
public:
int maxArea(vector<int>& height) {
int l = 0,n = height.size(),r = n-1;
int res = 0;
while(l<r){
res = max((r-l)*min(height[l],height[r]),res);
if(height[l]>height[r]){r--;}//考虑好指针移动条件
else {l++;}
}
return res;
}
};
【189. 旋转数组】
自己整理的看着最香:左/右旋转数组
2、双索引技术-滑动窗口
涉及连续子数组的问题,我们通常有两种思路: 一是滑动窗口、二是前缀和。
自己整理的看着最香:滑动窗口
【209. 长度最小的子数组】
滑动窗口就是要想好怎么滑,什么情况下滑,
怎么创建这个滑动窗口(sum+=nums[r];//第三题也是先由右指针去搞出窗口来),On
其实滑动窗口核心还是双指针,左右边界不就是两个指针决定的嘛
- 前缀和+二分查找:正整数数组保证前缀和单调性。前缀和之间的差,就是每个连续子数组。O(nlogn)
【3. 无重复字符的最长子串】
队列也好数组也好,其实就是一个控制左右的东东,右边满足条件就扩一下,左边不满足了就踢出去,然后注意边界条件!
**A1.unordered_set //res要放外面,不能放while里,不然如果没有重复字符res就一直是0了
A2.fre[256]**注意r=-1开始确保不漏掉第一个数
【219. 存在重复元素 II】
固定窗口大小,关键实现方法:
if(st.size()>k) {st.erase(nums[r-k]);}
【567. 字符串的排列】
//搞两个窗口出来,小的那个不动
//滑大的那个
【76. 最小覆盖子串】
- 滑动窗口+检查匹配;检查匹配可以优化;//还未做此题