双指针算法之左右指针

516 阅读3分钟

左右指针主要解决数组(或者字符串)中的问题,比如二分查找。

一、二分法查找

1、二分查找的框架

int binarySearch(int[] nums, int target) {
    int left = 0, right = ...;

    while (...){

        int mid = (right + left) / 2;

        if (nums[mid] == target) {

        ...

        } else if (nums[mid] < target) {

            left = ...

        } else if (nums[mid] > target) {

            right = ...

        }

    }

    return ...;
}

分析二分查找的一个技巧是:不要出现 else,而是把所有情况用 else if 写清楚,这样可以清楚地展现所有细节。本文都会使用 else if,旨在讲清楚,读者理解后可自行简化。其中...标记的部分,就是可能出现细节问题的地方,当你见到一个二分查找的代码时,首先注意这几个地方。后文用实例分析这些地方能有什么样的变化。

另外声明一下,计算 mid 时需要技巧防止溢出,建议写成: mid = left + (right - left) / 2,本文暂时忽略这个问题。

2、寻找一个数(基本的二分搜索)

这个场景是最简单的,可能也是大家最熟悉的,即搜索一个数,如果存在,返回其索引,否则返回其在数组中插入的位置。

public static int binarySearch(int[] array, int target) {

    int start = 0;

    int end = array.length - 1; //注意

    int mid = 0;

    while (start <= end) {//注意<=

        mid = (start + end) >> 1;

        if (array[mid] == target) {

        return mid;

        } else if (array[mid] < target) {

            start = mid + 1;//注意

        } else if (array[mid] > target) {

            end = mid - 1;//注意

        }

    }

    return end + 1;
}

对二分法进一步的思考参考:www.cnblogs.com/kyoner/p/11…

二、 数组求和

链接:leetcode-cn.com/problems/tw…

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。函数应该返回这两个下标值 index1 和 index2,其中index1 必须小于 index2。

说明:

返回的下标值(index1 和 index2)不是从零开始的。你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

示例:

输入: numbers = [2, 7, 11, 15], target = 9

输出: [1,2]

解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2

思路:

image.png 设置两个左右指针,一个纸箱头部,一致指向尾部,对着两个指针指向的数求和,如果和大于目标数,则将左指针右移,如果和小于目标数,则说明右指针需要右移,若两个指针相遇,即left==right都没有找到这两个数的话,则不存在两个数之和等于target

代码:

public static int[] position(int[] array, int target) {

    int left = 0;

    int right = array.length - 1;

    while (left < right){

        if (array[right] + array[left] == target){

        break;

    }else if (array[right] + array[left] < target){

        left++;//如果和大于目标数,则将左指针右移

    }else {

        right--;//如果和小于目标数,则说明右指针需要右移

    }

    return new int[]{++left, ++right};

}

2、三数之和

链接:leetcode-cn.com/problems/3s…

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]

输出:[[-1,-1,2],[-1,0,1]]

三数之和可以看成两数之和的高阶版本,只是有一些重复的情况需要额外处理。先对数组进行排序,然后遍历,那么,实际上三数之和就是在剩下的数组里面再找两个数的和为0-nums[i] image.png

 public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        int length = nums.length;
        List<List<Integer>> res = new ArrayList<>();
        for(int k = 0; k < length - 2; k++){
            //最小的数都大于0,直接结束
            if(nums[k] > 0)
                break;
            if(k > 0 && nums[k] == nums[k - 1])
                continue;
            int i = k + 1;
            int j = length - 1;
            while(i < j){
                int sum = nums[k] + nums[i] + nums[j];
                if(sum < 0){
                    while(i < j && nums[i] == nums[i + 1]){
                        ++i;
                    };
                    ++i;
                } else if (sum > 0) {
                    while(i < j && nums[j] == nums[i + 1]){
                        --j;
                    };
                    --j;
                } else {
                    res.add(new ArrayList<>(Arrays.asList(nums[k], nums[i], nums[j])));
                    while(i < j && nums[i] == nums[i + 1]){
                        ++i;
                    };
                    ++i;
                    while(i < j && nums[j] == nums[j - 1]){
                        --j;
                    };
                    --j;
                }
            }
        }
        return res;
    }
}

注意每次left++或者right--之后都有可能导致数组越界或者left越过right的可能性,所以每次都要判断left < right是否成立

3、反转数组

public static void reverse(int[] nums) {

    int tmp = 0;

    int left = 0;

    int right = nums.length - 1;

    while (left < right){

        tmp = nums[left];

        nums[left++] = nums[right];

        nums[right--] = tmp;

    }

}

数组是引用变量,所以并不需要有返回类型,只需要对当前数组进行翻转,函数外部的数组也会跟着翻转 【参考】 【1】leetcoe 【2】www.cnblogs.com/kyoner/p/11…

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。