剑指Offer 57、和为s的两个数字

113 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

题目:本题依然包括两个子问题。

  1. 输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
  2. 输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

解题思路

对于第一问,有点像两数之和,所不同的是本题的序列是有序的,那除了双层循环的方法之外,我们还可以使用哈希表和双指针来解决本题。

  1. 哈希表,每次检查哈希表中是否存在target-num是否已经在哈希表中,如果存在则numtarget-num即为一对正确结果,遍历完序列即可,时间复杂度和空间复杂度都为O(N)O(N)。代码如下:

    public int[] twoSum(int[] nums, int target) {
        HashSet<Integer> set = new HashSet<>();
        for (int num : nums) {
            if(!set.contains(target-num)){
                set.add(num);
            }else {
                return new int[]{num, target-num};
            }
        }
        return new int[0];
    }
    
  2. 因为数组已经有序,因此我们可用双指针的方式来得到最终结果,通过比较两个指针和和最终target的大小来决定指针的移动方向,可得代码如下:

    public int[] twoSum(int[] nums, int target) {
        int i=0, j=nums.length-1;
        while(i<j){
            if(nums[i] + nums[j] == target){
                return new int[]{nums[i], nums[j]};
            }else if(nums[i] + nums[j] > target){
                j--;
            }else {
                i++;
            }
        }
        return new int[0];
    }
    

对于第二问,题目要求输出的是连续正整数序列,那本题就相对容易了,最简单的方法是使用滑动窗口的方式,每次比较窗口中元素的数值和target的大小,通过大小来决定窗口的移动即可,可得代码如下:

public int[][] findContinuousSequence(int target) {
    ArrayList<int[]> list = new ArrayList<>();
    int value = 0;
    for(int i=1,j=1;i<target;i++){
        value += i;
        while(value>target){
            value -= j++;
        }
        if(value == target){
            int[] res = new int[i - j + 1];
            for(int m=0;m<i-j+1;m++){
                res[m] = j+m;
            }
            list.add(res);
        }
    }
    return list.toArray(new int[0][]);
}