0
点赞
收藏
分享

微信扫一扫

二分法查找有序数组arr中,大于等于k的最左侧、最右侧的位置

二分法查找有序数组arr中,大于等于k的最左侧的位置

提示:二分法系列文章
最基础的二分法:
二分法查找有序数组中的k所在的位置


文章目录

二分法查找有序数组arr中,大于等于k的最左侧的位置

查找arr中大于等于k的最左侧的位置。


一、审题

示例:arr=1 1 1 2 2 2 3 3 3 3 4 4 5 5 5,k=2
从左往右数,>=2的最左侧的位置就是i=4的位置
如果k很大很大呢,你是不是要从左往右数?????
肯定不行的
在这里插入图片描述


二、解题

显然,和之前说的一样,有序数组,查找某一个数k,一定不能遍历o(n)
需要二分查找,如果arr中有很多重复的元素,那你可能突然某一个mid瞬间找到了k
但是此时很多数都是k,我们需要最左侧那个位置,那就需要继续向左逼近

(1)最开始,mid=(L+R)/2=14/2=7,发现arr[mid]=3>=k,显然,k可能还会在mid左边,令R=mid-1=6,此时记下>=2的最左侧位置j=mid=7;
(2)然后,mid=(L+R)/2=6/2=3,发现arr[mid]=2>=k,显然,k可能还会在mid左边 ,令R=mid-1=2,此时记下>=2的最左侧位置j=mid=3;
(3)然后,mid=(L+R)/2=2/2=1,发现arr[mid]=1<k,显然,k可能还会在mid右边 ,令L=mid+1=2,此时不管j;
(4)然后,mid=(L+R)/2=2+2/2=2,发现arr[mid]=1<k,显然,k可能还会在mid右边 ,令L=mid+1=3,此时不管j;
显然L>R了,挂!
在这里插入图片描述
发现了没,实际上,每次都是二分查找,不断逼近最左侧的>=k的那个位置
没有重复的k,可以找找到唯一位置mid
有重复的k的话,那要不断地让k逼近左边,然后记忆>=k的那个元素的位置j,直到最后L>R了,j就是最左侧的位置
自己画个过程比比,看懂我的不代表你能懂,能写,自己动手写写,一下子就明白了

手撕代码:

//寻找<=k的最左侧的位置
    public static int findLessThanKmostLeftIndex(int[] arr, int L, int R, int k){
        int j = -1;
        while (L <= R){
            int mid = L +((R - L) >> 1);
            if (arr[mid] >= k) {
                j = mid;
                R = mid - 1;//可能还需要继续去左边找,你不是要最左边的位置吗?
            }else {//arr[mid] < k
                //如果mid太小,显然还需要去mid右边找
                L = mid + 1;
            }
        }

        return j;
    }

这个的暴力解,肯定也是不行的,N太大,失效了

//暴力解法
    public static int findRNum1(int[] arr, int value){
        int index = -1;
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] > value){
                index = i-1;
                break;//刚刚到最右边,就停止
            }
        }
        return index;
    }

有序数组中arr中,找<=k最右侧的位置j

那么相同的:
在有序数组中arr中,找<=k最右侧的位置j,同样的做法。
arr=2,2,2,2,3,3,3,3,4,4,4,4,当k=3时,找到最右边那个3的位置,即j=7

那么这时候要找得最右位置,那如果找到了mid,发现arr[mid]<=k时,记录j=mid,而且还有可能需要继续向右逼近,故记录L=mid+1,继续去右边找。
但是发现arr[mid]>k时,压根不需要右边找,k肯定在左边,即令R=mid-1,j不操作。
手撕代码一个道理:

//二分思想解法
    //有序数组中,找<=value最右侧的位置比如
    // arr==222222333334444444,当value==3时,找到最右边那个3的位置
    public static int findLessThanValue2(int[] arr, int value){
        int L = 0;
        int R = arr.length-1;
        int index = -1;

        while (L <= R){
            int mid = L +((R - L)>> 1);//求中点
            if(arr[mid] > value) {
                R = mid -1;//比value大,则继续找左边
            }else {//<=value 继续往右边找,直到L==R
                index = mid;
                L = mid +1;
            }
        }
        return index;
    }

暴力解在N很大时就失效了,半天找不到。

//暴力解法:
    public static int findLessThanValue1(int[] arr, int value){
        int index = -1;//记录对号
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] == value+1) {
                index = i-1;//刚找到value,左边位置就是最右边值
                break;//提前突出
            }
        }
        return index;
    }

测试代码:

public static void main(String[] args) {
        int[] arr = {2,2,2,2,3,3,3,3,4,4,4,4};
        int value = 3;

        //暴力解法,正确的解法
        int pos = findLessThanValue1(arr, value);
        System.out.println(pos);
        //优化算法,二分思想
        int pos1 = findLessThanValue2(arr, value);
        System.out.println(pos1);
        System.out.println(pos == pos1 ? "wright!":"wrong!!!");

        int pos2 = findLessThanKmostLeftIndex(arr, 0, arr.length - 1, 3);
        System.out.println(pos2);

    }

总结

提示:重要经验:

1)二分查找有序数组中<=k的最左侧,或者最右侧位置,非常容易
2)理解透二分查找就能很容易手撕出代码来。

举报

相关推荐

0 条评论