剑指 offer 面试题 57 和为 s 的两个数字 || 和为 s 的连续正数序列 (对撞指针、数学方法)

这篇博客介绍了如何使用对撞双指针算法解决两个和为s的数字搜索问题以及找到和为s的连续正数序列。在排序数组中,通过调整双指针寻找目标和,复杂度为O(n)。同时,文章还探讨了连续正数序列的解题思路,包括滑动窗口和数学方法。
摘要由CSDN通过智能技术生成

和为 s 的两个数字(对撞双指针)

个人博客


输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]
题解
  • 对撞双指针(在有序序列的情况下)

    • 算法思想

      • 在排序数组下,利用对撞双指针

      • 正确性证明

        • 记每个状态为 S(i, j)S(i,j) ,即 S(i, j) = nums[i] + nums[j]S(i,j)=nums[i]+nums[j] 。假设 S(i, j) < targetS(i,j)<target ,则执行 i = i + 1i=i+1 ,即状态切换至 S(i + 1, j)S(i+1,j) 。

          • 消去行或者列都不会导致解的丢失(因为我们是从右下角开始的,如果右下角「0,5」都太大的话,那么「1,5」的和会更大,如「0,4」太小的话「0,1」更小,因此可以消去)
        • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ony3acsP-1597135088730)(/Users/apple/Library/Application Support/typora-user-images/image-20200811025653398.png)]

    • 复杂度分析

      • 时间复杂度 O(n)
      • 空间复杂度 O(1)
    class Solution {
        public int[] twoSum(int[] nums, int target) {
            int i = 0,j = nums.length -1;
            while(i < j){
                int sum = nums[i] + nums[j];
                if(sum > target)j--;
                else if(sum < target)i++;
                else return new int[]{nums[i],nums[j]};
            }
            return new int[0];
        }
    }
    

II 和为 s 的连续正数序列


输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。

序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

输入:target = 9
输出:[[2,3,4],[4,5]]
题解
  • 对撞双指针(滑动窗口)

    • 算法思想
      • 数组是递增序列,使用两个指针(前指针A、后指针B),指针的区间就是目标序列,目标序列的和即为 sum,目标值为 target
        • 当 sum 大于 target 的时候,A 指针向后移动
        • 当 sum 小于 target 的时候,B 指针向后移动
        • 当 sum 等于 target 的时候,记录下此时的序列,然后 A 指针向后移动
        • A 指针移动到target/2的位置就行了
      • 正确性证明
        • 当 sum 大于 target 时,证明 sum 不再需要 A 此刻指向「以及 A 之前」的 元素,因为序列是连续的「加上此刻的 A 都已经大于 target 了,加上前面的元素一定会大于 target」
    • 复杂度分析
      • 时间复杂度 O(n)
      • 空间复杂度 O(1)
    class Solution {
        public int[][] findContinuousSequence(int target) {
            ArrayList<int[]> arraylist = new ArrayList<>();
            List<Integer> list = new ArrayList<>();
            int i = 1,j = 1,sum = 0;
            while(i <= target/2){
                if(sum < target){
                    sum += j;
                    j++;
                }else if(sum > target){
                    sum -= i;
                    i++;
                }else{
                    int[] res = new int[j-i];
                    for(int k = i;k < j;k++)res[k-i]=k;
                    arraylist.add(res);
                    sum -= i;
                    i++;
                }
            }
            return arraylist.toArray(new int[arraylist.size()][]);
        }
    }
    
  • 等差数列求和解方程

    • 算法思想ugRDHc

      • 下面的**-1/2是改为1/2**,作者写错了
      • EL0qjS
    • 复杂度分析

      • 时间复杂度 O(n)
      • 空间复杂度 O(1)
  • 数学方法

    • 算法思想

      • 对于目标target,若其为连续m个整数之和,则存在起始数x,有:

        1
        1 1
        1 1 1
        减一个1,看能不能分成两份
        再减两个1,看能不能分成三份
        再减三个1,看能不能分成四份
        。。。
        
    • 复杂度分析

      • 时间复杂度 O(n) 不太确定
      • 空间复杂度 O(1)
    class Solution {
        public int[][] findContinuousSequence(int target) {
            List<int[]> list = new ArrayList<>();
            int i = 1;
            while(target > 0){
                target -= i++;
                if(target > 0 && target % i == 0){
                    int[] arr = new int[i];
                    for(int k = target/i,j = 0;j < i;k++,j++){
                        arr[j] = k;
                    }
                    list.add(arr);
                }
            }
          	Collections.reverse(list);
            return list.toArray(new int[list.size()][]);
        }
    }
    
总结
  • 连续数组的时候可以考虑使用双指针
  • 连续递增,且增速固定的可以考虑数学方式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值