无序两数之和
对于当前num[i],要找的是target-num[i],可利用集合逐渐构建搜索集合
class Solution {
public:
//逐渐构造搜索集合 VS 初始时即构造完全的搜索集
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> ret;
if(nums.size()<=1) return ret;
map<int,int>table;
for(int i=0 ; i<nums.size();++i){
if(table.count(target-nums[i])){
ret.push_back(table[target-nums[i]]);
ret.push_back(i);
return ret;
}
table[nums[i]]=i;
}
return ret;
}
};
有序数字两数之和!!!
采用双指针
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
vector<int> ret;
if(numbers.size()<=1) return ret;
int i=0,j=numbers.size()-1;
while(i<j){
int curr = numbers[i]+numbers[j];
if(curr == target){
vector<int>ret ={i+1,j+1};
return ret;
}else if(curr <target){
++i;
}else if(curr >target ){
--j;
}
}
return ret;
}
};
3数字之和!!!
将原数列排序之后,归纳为有序三元组的解法。则依次选取第一个数字后,后面两数按照有序序列两数之和求解。注意去重
//归纳为有序三元组的解法
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<vector<int>> result;
//有序三元组第一个数字
int n = nums.size();
for(int i = 0 ; i< nums.size();++i)
{
//第一个数字去重
if(i > 0 && nums[i] == nums[i-1]) continue;
if((i+2) < nums.size() && (nums[i] + nums[i+1] + nums[i+2] ) > 0 )break;
//当前的第一个数字 跟最后两个数字之和也< target ,也就是当前数字太小,直接增大第一个数
if(i < n-2 && (nums[i] + nums[n-1] + nums[n-2]) < 0) continue;
//有序三元组剩余2个数
int left = i+1;
int right = nums.size()-1;
while(left<right){
int sum = nums[i] + nums[left] + nums[right];
if(sum < 0) ++ left;
else if(sum > 0) --right;
else if(sum == 0){
result.push_back(vector<int>{nums[i], nums[left],nums[right]});
++left;
--right;
while(left <right && nums[left] == nums[left-1]) ++left;
while(left < right && nums[right] == nums[right+1]) --right;
}
}
}
return result;
}
};
最接近的3数字之和
思路同上一题,注意对最接近的判断。
class Solution {
public:
int twoSumCloset(int l,int r,vector<int>&nums,int target){
int i=l,j=r;
int ret=nums[i]+nums[j];
while(i<j){
int sum=nums[i]+nums[j];
if(abs(sum-target)<abs(ret-target)){
ret=sum;
}
if(sum<target) ++i;
else if(sum>target)--j;
else{
break;
}
}
return ret;
}
int threeSumClosest(vector<int>& nums, int target) {
if(nums.size()<3) return -1;
sort(nums.begin(),nums.end());
int retsum=nums[0]+nums[1]+nums[2];
for(int i=0;i<=nums.size()-3;++i){
if(i>0&&nums[i]==nums[i-1])continue;
int twosum = twoSumCloset(i+1,nums.size()-1,nums,target-nums[i]);
int bias=abs(target-nums[i]-twosum);
if(bias<abs(retsum-target)){
retsum=twosum+nums[i];
}
}
return retsum;
}
};
四数之和
思路与三数之和一致。
注意与回溯法求K数之和不同处是,这里的数字有正有负,如果回溯需要对每一个数字均选择或不选择。而此处内层采用双指针方法,效率高,不会超时。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
vector<vector<int>> result;
//有序四元组的第一个数
for(int i = 0 ; i < nums.size() ;++i) {
if(i > 0 && nums[i] == nums[i-1] ) continue;
if((i+3) < nums.size() && (nums[i] + nums[i+1] + nums[i+2] + nums[i+3] ) > target ) break;
//有序四元组的第二个数
for(int j = i+1; j < nums.size() ;++j)
{
if(j > (i+1) && nums[j-1] == nums[j]) continue;
if((j+2) < nums.size() && (nums[i] + nums[j] + nums[j+1] + nums[j+2] ) > target ) break;
//有序四元组的最后两个数
int left = j+1;
int right = nums.size()-1;
while(left < right)
{
int sum = nums[i] + nums[j] + nums[left] + nums[right];
if(sum > target) --right;
else if(sum < target) ++left;
else if(sum == target){
result.push_back(vector<int>{nums[i],nums[j],nums[left],nums[right]});
++left;
--right;
//跳过相同数
while((left < right) && (nums[left] == nums[left-1])) ++left;
while((left < right) && (nums[right] == nums[right+1])) -- right;
}
}
}
}
return result;
}
};
最大装水体积!!!
//对于一个木板对<i,j>其容积由较小者的高度决定,另一个较高者的高度即使更高也不影响。
//对于木板i,其能组成的最大容器是与>=height[i] 中距离最远的那个木板组成。
//因此,对于<i,j>中高度较小者A,其能组成的最大容积已经确定(由于i,j由两侧向内,假设A为i,此时j即为最远的那个>=height[i]的木板),因此此时可以直接跳过A。
class Solution {
public:
int maxArea(vector<int>& height) {
if(height.size()<2) return 0;
int i=0;
int j=height.size()-1;
unsigned long area= (j-i)*min(height[i],height[j]);
//对于一个木板对<i,j>其容积由较小者的高度决定,另一个较高者的高度即使更高也不影响。
//对于木板i,其能组成的最大容器是与>=height[i] 中距离最远的那个木板组成。
//因此,对于<i,j>中高度较小者A,其能组成的最大容积已经确定(由于i,j由两侧向内,假设A为i,此时j即为最远的那个>=height[i]的木板),因此此时可以直接跳过A。
while(i<j){
//对于i,j中较小者,其能组成的最大容积已经得到,可以跳过。
area=max(area,(unsigned long)(j-i)*min(height[i],height[j]));
if(height[i]<height[j]){
++i;
}else{
--j;
}
}
return area;
}
};
判断链表是否是回文子串
找到链表中点后,将链表后面一半翻转,在与前一半比较。
最长无重复字符子串!!!
这是一道DP题目。
假设dp[i] 以第i个字符结尾的最长长度,同时记录
s[i]上一次出现的位置。
若字符i第一次出现,则dp[i]=dp[i-1]+1
否则,最长长度不能超过到上一次出现的位置。因此,
dp[i]=min(dp[i-1]+1,i-last_pos);
class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector<int>character_pos(256,-1);
//dp[i] 以第i个字符结尾的最长长度
vector<int>dp(s.size(),0);
int ret=0;
for(int i=0;i<s.size();++i){
if(i==0) dp[i]=1;
else{
int last_pos = character_pos[s[i]];
//该字符尚未出现过
if(last_pos == -1){
dp[i]=dp[i-1]+1;
}else{
dp[i]=min(dp[i-1]+1,i-last_pos);
}
}
character_pos[s[i]]=i;
if(dp[i]>ret)ret=dp[i];
}
return ret;
}
};
双指针的滑动窗口问题
参考文章 : 滑动窗口框架