算法-leetcode-每日一题-给出数组中和为target的索引(该数组为升序数组)

给出一个按升序排列的数组,返回两个索引,这两个索引对应的数字和为一个特定的值
假定上述问题只有一个解决方案,并且只能遍历数组一次

Example:
Input: numbers = [2,7,11,15], target = 9
Output: [1,2]
Explanation: The sum of 2 and 7 is 9. Therefore index1 = 1, index2 = 2.

分析:该题是https://blog.csdn.net/wujingchangye/article/details/88541022的进阶,加了一个该数组为升序数组的条件。一般,给出数组为升序,一定要联想到索引法。就是在数组的头尾各放一个指针,一个向前一个向后,找到后就返回。 按照上面的思路给出代码:

    public static int[] twoSum_inputIsSort(int[] numbers, int target) {
        int left = 0;
        int right = numbers.length - 1;
        while (left + 1 < right) {
            int cur = numbers[left] + numbers[right];
            if(cur == target) {
                return new int[]{left, right};
            }
            if(cur > target) {
                right = right - 1;
            } else {
                left = left + 1;
            }
        }
        return new int[]{left, right};
    }

上面的代码看过去好像很完美,但事实上并不是最优解,问题在于没有充分利用有序这个条件。当cur > 或< target时,left或right只进行了1的移动,这是非常慢的,如果numbers为{1, 3, 5, 7, 9, 90, 93, 95, 97, 99},则效率非常差,因此我们要想到二分法 通过二分法对right和left进行修正,从而进一步提高效率。

	public static int[] twoSum_inputIsSort(int[] numbers, int target) {
        //基本思想,用两个指针一头一尾,向前寻找,在向前或者向后的过程中用二分法查找
        int left = 0;
        int right = numbers.length - 1;
        while (left + 1 < right) {//因为left必须小于right,所以此处+1可以避免left==right
            int cur = numbers[left] + numbers[right];
            if (cur == target) {
                return new int[]{left + 1, right + 1};
            }
            if (cur < target) {//为当前右值用二分法查找一个左值,如果不存在,找一个比target大的数的下标,这样下个循环才能继续调整右值查找目标
                left = leftMove(numbers, target - numbers[right], left + 1, right);
            } else {//为当前左值用二分法查找一个右值,如果不存在,找一个比target小的数的下标,这样下个循环才能继续调整左值找到目标
                right = rightMove(numbers, target - numbers[left], left, right - 1);
            }
        }
        return new int[]{left + 1, right + 1};
    }
    private static int leftMove(int[] num, int target, int left, int right) {//1、找一个等于target数下标;2、找一个比target大的数的下标       
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (num[mid] == target) {//如果能找到则直接返回mid
                return mid;
            }
            //如果找不到,一定要返回大于leftTarget的最小值
            if (num[mid] > target) {//如果大于目标值,则一定记录mid,因为该值可能是大于leftTarget的最小值
                right = mid;
            } else {//mid比需要的左值小,则缩小范围,把right变为mid
                left = mid + 1;
            }
        }
        return left;
    }
    private static int rightMove(int[] num, int target, int left, int right) {//1、找一个等于target数下标;2、找一个比target小的数的下标
        while (left < right) {
            int mid = left + (right + 1 - left) / 2; //当数组个数为偶数时取右边的中值 都是取中值,差别就在于是左边得到mid+1还是右边mid-1,否则死循环
            if (num[mid] == target) {
                return mid;
            }
            //如果找不到,一定要返回小于rightTarget的最大值
            if (num[mid] > target) {//如果大于目标值,则right左移
                right = mid - 1;
            } else {//如果小于目标值,则一定要记录mid,因为该值可能是小于rightTarget的最大值
                left = mid;//因为这里是将mid赋于left,因此上面计算mid,一定要取右边mid,否则死循环
            }
        }
        return left;
    }

你可能感兴趣的:(算法)