排序数组中的两个数字之和

题目:输入一个递增排序的数组和一个值k,请问如何在数组中找出两个和为k的数字并返回它们的下标?假设数组中存在且只存在一对符合条件的数字,同时一个数字不能使用两次。例如,输入数组[1,2,4,6,10],k的值为8,数组中的数字2与6的和为8,它们的下标分别为1与3。

我们最容易想到的方法是,先扫描到数字i,再依次判断其余数字之和是否等于k,假设数组长度为n,因为每个数字要和其他n-1个数字组合,所以这种解法的时间复杂度是O(n^2)。

利用二分查找优化

同样的,先扫描到数字i,再判断数组中是否存在另一个数字k-i,不过因为数组是递增的,所以我们没必要从头到尾扫描每个数字,这里可以用二分查找进行优化。因为二分查找时间复杂度为O(logn),因此优化后解法的时间复杂度是O(nlogn)。

    public static int[] twoSum1(int[] numbers, int target) {
        int[] result = new int[2];

        for (int i = 0; i < numbers.length; i++) {
            //计算k-i的差值
            int complement = target - numbers[i];
            //在右侧有序部分二分查找差值
            int index = binarySearch(numbers, complement, i + 1, numbers.length - 1);

            if (index != -1) {
                //如果找到了差值
                result[0] = i;          //当前数字的下标
                result[1] = index;      //差值的下标
                break;
            }
        }
        return result;
    }
    //二分查找
    private static int binarySearch(int[] numbers, int target, int left, int right) {
        while (left <= right) {
            //获取中间元素的下标
            int mid = left + (right - left) / 2;
            if (numbers[mid] == target) {
                //差值下标
                return mid;
            } else if (numbers[mid] < target) {
                //说明差值在右侧,缩小查找范围到右侧部分
                left = mid + 1;
            } else {
                //说明差值在左侧,缩小查找范围到左侧部分
                right = mid - 1;
            }
        }
        return -1;
    }

使用哈希表查找

我们可以用空间换时间,使用一个哈希表来存储数组中的数字和对应的下标。我们遍历数组中的每个数字,对于每个数字,我们计算目标值与当前数字的差值complement。然后,我们在哈希表中查找是否存在该差值。哈希表中查找一个数字的时间复杂度为O(1),因此,时间复杂度是O(n),不过,该解法需要一个大小为O(n)的哈希表,因此空间复杂度是O(n)。

    public static int[] twoSum2(int[] numbers, int target) {
        int[] result = new int[2];
        //创建一个哈希表
        Map<Integer, Integer> map = new HashMap<>();

        for (int i = 0; i < numbers.length; i++) {
            //计算与当前数字的差值
            int complement = target - numbers[i];
            //判断差值是否存在于哈希表中
            if (map.containsKey(complement)) {
                //如果找到了差值
                result[0] = map.get(complement);    //获取差值在数组中的下标
                result[1] = i;                      //当前数字在数组中的下标
                break;
            }
            //将当前数字和下标放入哈希表中
            map.put(numbers[i], i);
        }

        return result;
    }

使用双指针

我们可以定义一个左指针i指向数组的开头,一个右指针j指向数组的末尾。然后我们不断地向中间移动左指针和右指针,直到找到和为k的两个数字或者左指针大于等于右指针为止。

在每次移动指针时,我们比较当前两个指针指向的数字之和与目标值target的大小。如果sum等于target,则找到了答案。如果sum小于target,则说明左指针的值太小,需要向右移动左指针。如果sum大于target,则说明右指针的值太大,需要向左移动右指针。

    public static int[] twoSum3(int[] numbers,int target){
        int i = 0;                          //左指针,开始指向数组第一个(最小)元素
        int j = numbers.length - 1;         //右指针,开始指向数组最后一个(最大)元素
        //循环退出条件左指针大于等于右指针或者两指针指向数字之和等于target
        while (i < j && numbers[i] + numbers [j] != target){
            if (numbers[i] + numbers[j] < target){
                //两指针指向数字之和小于target,我们会希望数字大一些
                i++;
            }else {
                //两指针指向数字之和大于target,我们会希望数字小一些
                j--;
            }
        }
        int[] result = new int[]{i,j};
        return result;
    }

该算法只有一个while循环,循环次数最多为数组长度,因此该解法时间复杂度为O(n),而空间复杂度是O(1)。

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不过是条河鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值