剑指 Offer 57-I/57-II 和为s的两个数字/和为s的连续正数序列C++


2021/2/5二刷
57-II的sum可以动态改变,没有必要调用多次等差数列公式

class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target) {
        if(target < 3) return {{}};
        vector<vector<int>> ans;
        int left = 1, right = 2;
        int sum = 3;
        while(left < right) {
            if(sum == target) {
                 ans.push_back(re(left,right));
            }
            if(sum < target) {
                right++;
                sum += right;
            } else {
                sum -= left;
                left++;
            }
        }
        return ans;
    }
    vector<int> re(int left, int right) {
        vector<int> tmp;
        for(int i = left; i <= right; i++) {
            tmp.push_back(i);
        }
        return tmp;
    }
};

需要注意一下边界的问题
这样写会把自己也包括进去

class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target) {
        if(target < 3) return {{}};
        vector<vector<int>> ans;
        int left = 1, right = 2;
        int sum = left;
        while(left < right) {
            if(sum == target) {
                 ans.push_back(re(left,right));
            }
            if(sum < target) {
                sum += right;
                right++;
            } else {
                sum -= left;
                left++;
            }
        }
        return ans;
    }
    vector<int> re(int left, int right) {
        vector<int> tmp;
        for(int i = left; i < right; i++) {
            tmp.push_back(i);
        }
        return tmp;
    }
};

57-I

题目描述

在这里插入图片描述
前面做过两题一摸一样的,思路略

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
            int i = 0, j = nums.size() - 1;
            while(i < j) {
                if(nums[i] + nums[j] > target) j --;
                else if(nums[i] + nums[j] < target) i ++;
                else return vector<int>{nums[i], nums[j]};
            }
            return {};
    }
};

57-II

在这里插入图片描述

思路 双指针法/滑动窗口法

先记录一下自己的错误想法,思路大概是先让窗口包括可能取到的最大连续序列,然后不断缩小窗口,每次缩小左或者右通过判断子序列和和target之差来决定,代码会导致死循环,并且找到最长解之后就找不到第二组解

class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target) {
        int i = 1, j = target;
        vector<vector<int>> anss;
        while(i < j) {
            if(sum(i,j) < target) { 
                if(target - sum(i, j) >= i && target - sum(i, j) < j) i --;
                else if(target - sum(i, j) >= j) j ++;
            }
            else if(sum(i, j) > target) {
                if(sum(i , j) - target >= j) j --;
                else if(sum(i, j) - target < j && sum(i, j) - target >= i) i ++;
            }
            else {
                anss.push_back(re(i, j));
                i++;
            } 
        }
        return anss;
    }
    int sum(int i, int j) {
        return (i + j) * (j - i + 1) / 2;
    }
    vector<int> re(int i, int j) {
        vector<int> ans;
        for(int k = i; k <= j; k ++) ans.push_back(k);
        return ans;
    }
};

总之是非常蠢的代码
但是只要稍微改一下思路就正确了,让指针 L R都从右开始 L = 1 R = 2,构成一个左闭右闭区间[1,2],
然后窗口只要一直往右移动就行了。

  1. sum < target 说明窗口内元素总合不够target大小,移动右指针R扩大窗口
  2. sum > target 说明窗口内元素总和过大,移动左指针L缩小窗口
  3. sum = target 返回一组解。
class Solution {
public:
   vector<vector<int>> findContinuousSequence(int target) {
       //初始[1,2]左闭右闭区间
       int l = 1;int r = 2;
       vector<vector<int>> anss;
       while(l < r) {
           int res = sum(l, r);
           if(res == target) { 
               anss.push_back(re(l, r));
               l ++;
           }
           else if(res < target) r++;
           else l++;
       }
       return anss;
   }
   int sum(int i, int j) {
       return (i + j) * (j - i + 1) / 2;
   }
   vector<int> re(int i, int j) {
       vector<int> ans;
       for(int k = i; k <= j; k ++) ans.push_back(k);
       return ans;
   }
};

这里要注意的是,

  1. 关于得到结果时
  if(res == target) { 
  	anss.push_back(re(l, r));
   	l ++;
 }

在得到答案之后,l一定要右移,否则又会死循环。为什么是l右移呢,因为以l为开头的解有且只有一个。
2. 关于终止条件

while(l < r)

当l == r时,r一定处于 target/2 + 1的位置 ,这时候继续往右已经没有意义。置于为什么r会处在这个位置分析如下:

  1. r不可能处在target/2 + 2, 因为target/2 + 1的最小窗口即[target/2, target/2+1]的合一定大于等于target,所以r不会再右移
  2. r一定会移动target/2 + 1, 因为最接近target且一定小于target的是[target/2 -1, target/2]而通过滑动窗口的正确性我们能知道区间一定会探索这个位置,所以r将会变成target/2 + 1。
    在这里插入图片描述
    时间复杂度O(target)
    空间复杂度O(1)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值