剑指 offer:和为S的连续正数序列 & 和为S的两个数字

 和为S的连续正数序列

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck! 

采用滑动窗口原理,双指针记录滑动窗口的边界,当总和小于sum,扩大窗口,大指针继续+。当总和小于sum,缩小窗口,小指针+.

这种解法其实可以理解为双重for循环的剪枝后优化版。low相当于外循环的i,high相当于内循环的j。

public class Solution {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> allRes = new ArrayList<ArrayList<Integer>>();
        int high = 2,low = 1; 
        while(high > low){
            int cur_sum = (high + low) * (high - low + 1) / 2;
            if(cur_sum < sum)  high++;     
            else if(cur_sum > sum)  low++; 
            else {
                ArrayList<Integer> res = new ArrayList<Integer>();
                for(int i = low; i <= high; i++)
                   res.add(i);
                allRes.add(res);
                low++;
            }                                      
        }
        return allRes;
    }
}

和为S的两个数字 

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。 

传统做法: 

public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> arr_sum = new ArrayList<Integer>();
        int min = 10000;
        int mlow = 0, mhigh = 0;
        for(int i = 0; i < array.length; i++){
            for(int j = i; j < array.length; j++){
                if(array[i] + array[j] == sum){
                    if(min > array[i] * array[j]){
                        min = array[i] * array[j];
                        mlow = array[i];
                        mhigh = array[j];
                    }
                }
            }     
        }
        if(mlow == 0 && mhigh == 0) return arr_sum;
        arr_sum.add(mlow);
        arr_sum.add(mhigh);
        return arr_sum;
    }
}

优化后的做法:

跟上一题的解法及其相似,双指针记录滑动窗口的边界。如果当前和 > sum,则high--,否则,low++.

为什么在第一次找到i,j时可以输出呢,因为越往中间走成积越大。

public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> arr = new ArrayList<Integer>();
        int i = 0, j = array.length -1;
        while(i < j){
            if(array[i] + array[j] == sum){
                arr.add(array[i]);
                arr.add(array[j]);
                break;
            }
            while(i < j && array[i] + array[j] > sum) --j;
            while(i < j && array[i] + array[j] < sum) ++i;
        }
        return arr;
    }
}

 对于这两题的总结

         看了第一题的思路还是做不出第二题,原因在于low和high的位置放错了,一开始把low和high放在了array[0],而high++和low++都会导致零时和array[i] + array[j]的增大,即不管怎么改变high和low都只会导致和比原来的小。反观第一题,high++导致临时的连续和增大,low++会导致临时的连续和减小,是双向的。故第二题应该把high和low放在数组的两端,low++会导致临时和增大,high--会导致临时和减小。

猜你喜欢

转载自blog.csdn.net/raylrnd/article/details/82725761