一:有序数组两数之和
?
?注意:
- 这里要求两个索引是不同的,不能用一个索引取两次凑target,而且索引从1开始
- 存在解?唯一解?
法一:看见有序,想到二分查找
int binarySearch(vector<int>& nums,int target){
//传入数组和查找值,返回查找结果
int L=0;
int R=nums.size()-1;
while(L<=R){//范围:[L,R]---左闭右闭
int mid=(L+R)/2;
if(nums[mid]==target) return mid;
else if(nums[mid]>target) R=mid-1;
else L=mid+1;
}
return -1;//没找到
}
本题思路:从头遍历数组,然后取后面二分查找符合题意的
class Solution {
public:
int binarySearch(vector<int>& nums,int target,int L){
//补充左边界
int R=nums.size()-1;
while(L<=R){
int mid=(L+R)/2;
if(nums[mid]==target) return mid;
else if(nums[mid]>target) R=mid-1;
else L=mid+1;
}
return -1;
}
vector<int> twoSum(vector<int>& numbers, int target) {
int res[2]={0};
for(int i=0;i<numbers.size();i++){
//从i+1的位置,去找target-numbers[i]
int p=binarySearch(numbers,target-numbers[i],i+1);
if(p>0){
//都加1,索引从1开始
res[0]=i+1;
res[1]=p+1;
}
}
return vector<int>(res,res+2);//数组长是2
}
};
法二:对撞指针?
首先将i,j指针指向有序数组两端,如果恰好相等,满足题意
如果和小了,应该扩大数值,很自然,只能将i右移
?
?如果和大了,应该缩小数值,相应的将j左移
?最后,一定在某个位置得到所求(题目保证必有解)
vector<int> twoSum(vector<int>& numbers, int target) {
//初始化指向数组两端的指针:i和j
int i=0;
int j=numbers.size()-1;
while(i<j){//题目不能让i,j对应一个元素
if(numbers[i]+numbers[j]==target){
int res[2]={i+1,j+1};
return vector<int>(res,res+2);
}
else if(numbers[i]+numbers[j]<target) i++;
else j--;
}
//假设没有解,抛出个一场
throw invalid_argument("The input has no solution!");
}
思路很清晰,如果没有边界,选择抛出异常也是个很好的选择
?二:最小子数组
?注意:连续子数组,如果不存在符合条件的子数组,返回?0 ?
? ? ? ? 如图:指针i和j对应了一个区间,起始只要sum比所求小,j就不断右移,该窗口就一直扩大,直至满足了条件----和大于目标值,记录此时的窗口长度。
?
? ? ? ? 此时,i不断右移,目的是缩小窗口,在原来和的基础上求最小。如果出现sum小于目标值,i就停止,这时候j又可以右移了。如此循环
这种双索引技术叫“滑动窗口”,利用了连续的定义,窗口值一直在变,但是i,j在数组滑动的过程,逐渐找到所求 |
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int minLen=nums.size()+1;//开始无解
//双索引i,j形成连续子数组和的窗口:[i,j],初始化时候没窗口
int i=0;
int j=-1;//右小于左时没窗口
int sum=0;
/*串口滑动*/
while(i<nums.size()){//i可以移动,j一定可以
/*每次循环移动j或i----二选一,一步步移动*/
if(j+1<nums.size()&&sum<target) //++j也要满足取值范围
sum+=nums[++j];//先移动再求和
else //sum>=target
sum-=nums[i++];//先减值再移动
/*满足条件更新minLen*/
if(sum>=target)
minLen=min(minLen,j-i+1);//串口大小:索引相减加一
}
//无解
if(minLen==nums.size()+1) return 0;
return minLen;
}
};
仔细体会代码思想
三:无重复字符的最长子串?
?
?这个思路和滑动窗口一致:
既然只要求大小,那么用查找表存无重复的元素:
滑动过程-----
- 查找表不存在当前j指向元素,j不断右移扩大窗口;
- 如果找到了重复字符,那么j不断左移缩小窗口。
由于取最大,每次移动一次更新一次目标长度值
class Solution {
public:
int lengthOfLongestSubstring(string s) {//用set实现
/*初始化变量:i,j窗口指针、len返回值*/
int i=0;
int j=-1;//[i,j]无元素
int len=0;//max
set<char> record;//set查找表
while(i<s.length()){//i可继续移动
if(j+1<s.length()&&record.find(s[j+1])==record.end()){//找不到
//j可以向右移动
record.insert(s[++j]);
}
else{//查找表有重复元素
record.erase(s[i++]);//i右移
}
//更新len
len=max(len,j-i+1);
}
return len;
}
};
class Solution {
public:
int lengthOfLongestSubstring(string s) {//用set实现
/*初始化变量:i,j窗口指针、len返回值*/
int i=0;
int j=-1;//[i,j]无元素
int len=0;//max
map<char,int> record;//map查找表
while(i<s.length()){//i可继续移动
if(j+1<s.length()&&record[s[j+1]]==0){//找不到
//j可以向右移动
record[s[++j]]++;
}
else{//查找表有重复元素
record[s[i++]]--;//i右移
}
//更新len
len=max(len,j-i+1);
}
return len;
}
};
|