[Java算法日记]二分法

目录

一.典型代码(也是最常用的代码)

二.通俗解释以及特殊情况

1.通俗解释

2.关于为什么循环条件有i=j

结论1: 二分算法搜索一次能够去除一半及以上的搜索目标

结论2 : 回家睡大觉的情况(根本没有找到鱼停止搜索)只有左标在右标的右边,就是i>j的情况

(循环条件写成i<=j)

结论3:搜索到i=j的情况(也就是该算法到了最后一步):

3.关于有序数组中出现两条或者多条相同的鱼的情况

结论:二分法查找的数据中,如果查找的数据在数组中有多个,查找到的数据下标不一定是靠前的,如果想要找靠前或者靠后的目标,本章后面的内容会有。

三.把“/”换成“>>>”

四.算法时间复杂度分析:时间复杂度以及空间复杂度

线性算法代码分析

二分查找代码分析

算法情况对比:3n+3与5log_2(n)+9   

直观的图像表达:绿色二分法,红色线性法

一些额外数学知识

空间复杂度:算法执行随着数据规模增大而增长的额外空间成本

五.第二种表达形式(替代版)(第二种表达方式与第三种表达方式不是很重要,可以跳过着两个大段)

六.第三种表达形式(平衡版)

七.Java中二分法的代码阅读

八.找最左边的目标值与最右边的目标值

二分查找LeftMost:找最左侧的目标数字(有多个相同的目标数字的情况)

二分查找RightMost:找最右侧的目标数字(有多个相同的目标数字的情况)

九.代码简化,可用于查找某一范围的值,或者求排名

十.力扣题目实战经验分享

35. 搜索插入位置:三种解法

34. 在排序数组中查找元素的第一个和最后一个位置:三种解法,一步一步改进的


一.典型代码(也是最常用的代码)

目标:在有序数组内(允许数组内有重复的元素),查找某个值,找到返回索引,找不到返回-1。

我的想法:直接循环array.length().fori,不过想要在有序数组之内找数,如果一个一个找,效率会很低

案例代码:

public class demo {
    public static void main(String[] args) {
        int target = 10;
        int[] arr = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90};
        System.out.println(binarySearchBasic(arr, target));
    }
    private static int binarySearchBasic(int[] arr, int target) {
        int i = 0, j = arr.length - 1;
        while (i <= j) {
            int m = (i + j)>>>1;
            int mid = arr[m];
            //目标在左边
            if (target < mid) {
                j = m-1;
                //目标在右边
            } else if (mid < target) {
                i = m+1;
                //找到目标
            } else {
                return m;
            }
        }
        return -1;
    }
}

 

 


二.通俗解释以及特殊情况

1.通俗解释

1.设置一个左标,一个右标,一个中间标(由左右标决定其位置)一条鱼(目标值):

2.如果左标在右标的右边,那就回家,不抓鱼了(左标与右标在同一个地方的情况下面再解释)

3.如果左标不在右边右边,执行下列3种情况:

4.鱼在中间标左边:中间标右边所有地方废弃,中间标变成右标,然后再把右标向左移动一位(因为原本的中间标没叉到鱼,所以向左移动一位),回到2

5.鱼在中间标右边:中间标左边所有地方废弃,中间标变成左标,然后再把左标向右移动一位(因为原本的中间标没叉到鱼,所以向右移动一位),回到2

6.鱼在中间标上面:直接抓住鱼返回,抓到鱼回家

2.关于为什么循环条件有i=j

关于左标i与右标j在同一个地方的情况:

案例1

01,左标0,右标1,中标0,鱼:1

中标没中,中标变左标+1:变成:1----左右中标:1

(两个目标--一次搜索之后减少到为一个目标(50%||或者搜索到(50%))

案例2

012,左标:0,右标:2,中标:1,鱼:2

中标没中,中标变左标+1:变成:2----左右中标:2

案例3

012,左标:0,右标:2,中标:1,鱼:0

中标没中,中标变右标-1:变成:0----左右中标:0

(三个目标--一次搜索之后减少到为一个目标(66.6%||或者搜索到(33.3%))

案例4

0123,左标:0,右标:3,中标:1,鱼:0

中标没中,中标变右标-1:变成:0----左右中标:0

案例5

0123,左标:0,右标:3,中标:1,鱼:3

中标没中,中标变左标+1:变成:2----左标:2,右标:3,中标:2

中标没中,中标变左标+1:变成:3----左右中标:3

案例6

0123,左标:0,右标:3,中标:1,鱼:2

中标没中,中标变左标+1:变成:2----左标:2,右标:3,中标:2

(四个目标,一次搜索之后减少到为一个目标(25%||两个目标(50%||搜索到(25%))

以此类推:

(五个目标,一次搜索之后减少到为两个目标(80%||搜索到(20%))

(六个目标,一次搜索之后减少到为两个目标(2/6||三个目标(3/6||搜索到(1/6))

可见:如果运气不好一直没叉中鱼:就会到左右中标三个标都在一个目标上的情况。

以上都是一定会有鱼的情况

如果说压根没有鱼(或者说鱼在1.5的地方),那就一定会出现i=j的情况:但是最后的中标上不会有鱼,循环在这个时候不会结束,再判断一次,使得i>j,循环结束

结论1: 二分算法搜索一次能够去除一半及以上的搜索目标
结论2 : 回家睡大觉的情况(根本没有找到鱼停止搜索)只有左标在右标的右边,就是i>j的情况
(循环条件写成i<=j
结论3:搜索到i=j的情况(也就是该算法到了最后一步):

        1.鱼的位置特殊(小保底一直歪)

        2.根本没有鱼(您抽错的是常驻池子)

3.关于有序数组中出现两条或者多条相同的鱼的情况

如果有两条鱼是我想要的,那就叉到那个选哪个:下标顺序不一定是靠前的

比如0123456789:

34是鱼,第一次叉4,中标,鱼4被抓(靠后的鱼)

23是鱼,第一次叉4,右标左移到3:第二次叉1,左标右移到2:第三次叉2:中标,鱼2被抓(靠前的鱼)

结论:二分法查找的数据中,如果查找的数据在数组中有多个,查找到的数据下标不一定是靠前的,如果想要找靠前或者靠后的目标,本章后面的内容会有。


三.把“/”换成“>>>”

        理由:之前的算法中:int m = (i + j) / 2;有问题!

        当数据量超过定义ijm的数据类型表示的最大值的1/2的情况下,如有可能循环到的(i+j)大于int类型所表示的最大值,基本数据类型越界会变成负数

案例代码

public class demo2 {
    public static void main(String[] args) {
        int i =0,j=Integer.MAX_VALUE;
        int m = (i+j)/2;
        System.out.println("右标的值:");
        System.out.println(j);
        System.out.println("中标的值:");
        System.out.println(m);
        //如果说要找的值在m的右边:就会出现越界的情况:补码相加(m+j):
        //01111111 11111111 11111111 11111111+
        //00111111 11111111 11111111 11111111=
        //10111111 11111111 11111111 11111110
        System.out.println("超过int类型范围的数据出错:");
        System.out.println((m+j)/2);
        //正整数“无符号”右移一位的效果和(m+j)/2一样,但是这里m+j是改变了数的正负
        //无符号右移可以解决这个问题,注意一定是无符号右移,也可以适用于更多的语言:如js!
        //无符号右移后的补码数据:
        //01011111 11111111 11111111 11111111
        System.out.println("使用无符号右移解决问题:");
        System.out.println((m+j)>>>1);
    }
}
不过,int类型的数据能表示21个亿,如果是long类型的数据可以表示42亿个int,这么多的数据我也没见过,我不知道这里是否真的很有必要使用无符号右移。不过这里给一个解释,后面都是用的无符号右移。

 


 

四.算法时间复杂度分析:时间复杂度以及空间复杂度

线性算法代码分析
    private static int linearSearch(int[] arr, int target) {
        for (int i = 0; i < arr.length; i++) {
            if (arr.length==target){
                return i;
            }
        }
        return -1;
    }
算法最坏情况:找不到目标,下面是代码执行统计:
元素个数n
int i=0;                    1
i<arr.length                n+1
i++                         n
if(arr.length==target)      n
return -1                   1
总共:3n+3
二分查找代码分析
    private static int binarySearchBasic(int[] arr, int target) {
        int i = 0, j = arr.length - 1;
        while (i <= j) {
            int m = (i + j)>>>1;
            if (target < arr[m]) { j = m-1;}
            else if (arr[m] < target) { i = m+1; }
            else {return m;}
        }
        return -1;
    }
算法最坏情况:找不到目标:如[2,3,4,5],找左边的数据1与找右边的数据6,那一个运行得更久呢?找6
因为m是向下取整的,导致找右边的数据会比找最左边的数据花费时间更多
数据共有n个:
如果需要找的数据在最右边,n的变化:n=n/2
如果数据在最左边,n的变化:n=(n-1)/2
这里按数据在最右边的情况下对代码执行统计:
    元素个数                循环次数
    1                       1
    2-3                     2
    4-7                     3
    8-15                    4
    16-31                   5
    32-63                   6
    最坏的情况就是向下取整的L=log_2(n)+1 次
    代码                                  运行次数
    int i = 0, j = arr.length - 1;          2
    return -1;                              1
    i <= j                                  L+1
    int m = (i + j)>>>1;                    L
    if (target < arr[m]) { j = m-1;}        L
    else if (arr[m] < target) { i = m+1; }  2L
    else {return m;}
    共5log_2(n)+9次
算法情况对比:3n+3与5log_2(n)+9   


    n=5时:线性算法:18t
            二分法:19t
    n=6时:线性算法:21t
            二分法:19t
    往后二分算法都优于线性算法

直观的图像表达:绿色二分法,红色线性法

可见,当n>=6的时候,二分法的最坏搜索情况所需要的次数就一直比线性查找的要少了

一些额外数学知识

渐进上界是算法执行的最差情况

渐进下界是算法执行的最佳情况

渐进紧界是上界与下界的结合

空间复杂度:算法执行随着数据规模增大而增长的额外空间成本

二分法用到了三个变量i,j,m

用到的空间复杂度就是3个int

所以空间复杂度是O(1)


五.第二种表达形式(替代版)(第二种表达方式与第三种表达方式不是很重要,可以跳过着两个大段)

先看代码改动:

//第二版二分法
public class demo1 {
    public static void main(String[] args) {
        int target = 10;
        int[] arr = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90};
        System.out.println(binarySearchAlternative(arr, target));
    }
    private static int binarySearchAlternative(int[] arr, int target) {//替代版本
        //第一处改动
        int i = 0, j = arr.length;
        //第二处改动
        while (i < j) {
            int m = (i + j)>>>1;
            int mid = arr[m];
            if (target < mid) {
                //第三处改动
                j = m;
            } else if (mid < target) {
                i = m+1;
            } else {
                return m;
            }
        }
        return -1;
    }
}

现在来讲讲第二种表达方式与第一种表达方式的不同:

把右标设置在要查找数据的数组的最右边的数据下标+1,也就是说,右标永远不会有鱼,只有左标上会右鱼,右标代表的是边界,所以我更愿意把这中方式比喻成:左标,中标,右界。

1.设置左标在数组开头,右界在数组最右数据的下标+1处。

2.如果左标在右界左边:则代表没搜完,继续向下捕鱼,否则代表没鱼了,回家睡大觉

3.如果鱼在中标左边:则移动右界到中标的位置,回到2

4.如果鱼在中标右边:则移动左标到中标位置并向右移动一位,回到2

5.如果鱼在中标上面:叉中鱼,回家吃席,返回下标。

6.都到这里了,说明没得鱼了,回家睡大觉吧,返回-1。

不同的地方有三处:都写了注释

第一处:j的值设为数组的容量,j不再代表数组的最右值的下标,而是该下标+1:也就是说j作为数组的边界,j所在的下标一定不是查找目标,而是搜索j所在下标的左边,当j移动的时候,j一定不会踩到目标上面。而i不变:也就是说,i所在的下标还是能够有可能存在目标搜索值

第二处:循环条件改为i<j,由于j不会踩到目标上,所以失去了i=j的循环条件,当ij相邻的时候就可以进行最后一步判断:也就是判断i下标是不是要寻找的目标。如果这个地方写了等于,那就有可能陷入死循环(i==j且目标在m的左边,j不会在m处-1)

第三处:目标在m左边时j的变化:j不再变成m-1了:而是变成m。因为j永远不会代表搜索的目标的下标:而m也不是,m的左边有可能,所以j要变成m,算法继续在i及i的右边于j的左边之间搜索,如果j变成了m-1,那么j就有可能踩到目标,恰好m又不可能等于j


 

 

六.第三种表达形式(平衡版)

在基础版本的二分查找法中:target在最左边和在最右边需要查找的次数不一样,在最左边只用执行一次判断,在右边则要执行两次判断!(当然您的代码也有可能是先判断右边再判断左边,不过这样也是,查找最左边的数据与查找最右边的数据需要经过的判断语句代码大约是2:1)

该版本为平衡版本的二分查找:减少循环中的条件判断:这里的每次循环中只用条件判断一次,而前面两个版本的算法有可能要条件判断2次

案例代码:

public class demo4 {
    public static int binarySearch3(int[] arr,int target){
        int i =0,j =arr.length;
        //改动一
        while (i<j-1){
            int m =(i+j)>>>1;
            if(target<arr[m]){
                j=m;
            }else{
                //改动二
                i=m;
            }
        }
        if (arr[i]==target) return i;
        else return -1;
    }
}

这种就相当于不拿鱼叉了,一定要收回渔网捉鱼(您看看我的比喻到不到位)

1.设置左标在数组最左边,右界在数组最右边的数据下标出+1,中标在左标右界中间,这里左标上有可能有鱼,右界上绝对不会有鱼,中标失去鱼叉的攻击性,不会叉鱼,只是代表位置。

2.当左标与右边距离还没有相邻:执行下一步,否则就代表该收网了,看看有没有鱼

3.当目标在中标左边:移动右界到中标位置,回到2

4.当目标在中标上或者中标右边:移动左标到中标上,回到2 

5.到这里已经退出循环了,收网,看看有没有鱼,有鱼则返回中标位置,没有鱼返回-1

相较于第二版的二分法(替代版) 

第一处改动:i<j-1;

这行代码的意思就是ij相邻的时候就结束循环

这个和替代版本(第二种表现方式)的二分查找法不一样,替换版本执行到最后,最后的循环条件是:i=j-1(ij相邻,判断i就能够出结果),而这里的代码,当i=j-2的时候(就是说ij之间还有一个家伙),再进这个循环,把i?j变成ij相邻,接下来就只用在环循外面i是不是目标了(因为j是边界不代表搜索目标)

第二处改动:

这里是等于m,因为需要搜索的目标值可能就在m上面,以前能够直接返回m结束循环了,这个算法不行,i如果在m上则不能动,等待j靠过来即可,如果这个地方写了m+1,就有可能错过了

算法缺点:前面的两个版本的算法有可能找一次就找到了,但是这个必须要找到最后一步才能知道有没有鱼(也就是到左标i与右标j相邻的情况), 之间复杂度最好最坏的情况都是O(log(n))


 

七.Java中二分法的代码阅读

在Arrays.util中有Java的二分查找方法
ctrl+N(搜索类) ctrl+F12(查看方法)看看:它用的是二分查找的第一版
不同的是,最后它return的是:-(左标+1)

如果找到了目标数,直接插入到这个数这个下标就行(但是这里面对于多个相同查找目标的情况会出现一点问题,文章后面再讨论)
第一版的二分查找我们提到过:找不到的情况绝对会是左右中标重合在一起:这里我们讨论左右标重合之后的那一步:
假如数组123,鱼在2.5处:左右标到3,下一步:右标左移,左标还是3
假如数组123,鱼在1.5处:左右标到1,下一步:左标右移到2。
如果要把2.5插入123,就应该把3的位置给2.5
如果要把1.5插入123,就应该把2的位置给1.5
这下懂了,如果没找到,那么左标的位置,就是查找不到的数应该插入的地方
为什么返回-(左标+1)呢?负号能理解,但是为什么要+1呢?
是为了避免该数在数组最左边的情况:123,鱼在0处,左标会到0,返回左标的话就是0:代表找到该插入的地方了,返回0
返回的0有两种意思:一种是找到数字:另一种是应该插入数字到0,有歧义,所以这里会返回-(左标+1),或许这就和反码为什么要变补码的情况类似吧。
public class demo5 {
    public static void main(String[] args) {
        int target = 45;
        int[] arr = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90};
        int index=Arrays.binarySearch(arr,target);
        System.out.println(index);
        //插入数的操作
        if (index<0){
            int[] arr1 = new int[arr.length+1];
            int insertIndex=-index-1;
            //arrayCopy内数据的含义:被复制的数组,被复制的数组开始的下标,复制到的数组,复制到的数组的开始下标,复制的个数。
            System.arraycopy(arr,0,arr1,0,insertIndex);
            arr1[insertIndex]=target;
            System.arraycopy(arr,insertIndex,arr1,insertIndex+1,arr.length-insertIndex);
            for (int i = insertIndex+1 ;i<arr1.length ; i++){
                arr1[i]=arr[i-1];
            }
            //使用数组工具类变成String类型
            System.out.println(Arrays.toString(arr1));
        }
    }
}

 


 

 

八.找最左边的目标值与最右边的目标值

需求:在二分查找中查找到元素有多个,返回要查找的元素的最左边的(下标最小)下标
先想想:为什么会出现这样的情况:心急,抓到鱼就回去了,没有抓到最左边的鱼;是你要抓的鱼,先别返回,记录一下,看看左边有没有鱼
如123445678:第一次就抓到4,第二次到2第三次到3,到最后一次才抓到最左边的4:
所以想要抓到最左边的鱼:你还是得把算法执行到最后(也就是和前面所说的平衡版的二分查找模式一样,但是注意,平衡版的二分查找不一定能够找到最左边或者是最右边的目标数字!)

二分查找LeftMost:找最左侧的目标数字(有多个相同的目标数字的情况)

这个算法其实就是第一版的算法在找到了对应数字之后,继续向左边查找数字:找到什么程度后停止?左标>右标。

二分查找RightMost:找最右侧的目标数字(有多个相同的目标数字的情况)

这个算法其实就是第一版的算法在找到了对应数字之后,继续向右边查找数字

案例代码:

public class demo6 {
    public static void main(String[] args) {
        int target = 40;
        int[] arr = {0, 10, 20, 40, 40, 40, 40, 70, 80, 90};
        System.out.println(binarySearchRightMost(arr, target));
    }
    private static int binarySearchLeftMost(int[] arr, int target) {
        int i = 0, j = arr.length - 1;
        //改动1
        int candidate = -1;
        while (i <= j) {
            int m = (i + j) >>> 1;
            int mid = arr[m];
            if (target < mid) {
                j = m - 1;
            } else if (mid < target) {
                i = m + 1;
            } else {
                //改动2
                candidate = m;
                j = m - 1;
            }
        }
        //改动3
        return candidate;
    }
    private static int binarySearchRightMost(int[] arr, int target) {
        int i = 0, j = arr.length - 1;
        int candidate = -1;
        while (i <= j) {
            int m = (i + j) >>> 1;
            int mid = arr[m];
            if (target < mid) {
                j = m - 1;
            } else if (mid < target) {
                i = m + 1;
            } else {
                candidate = m;
                //与leftmost不同的地方
                i = m + 1;
            }
        }
        return candidate;
    }
}
在leftMost中:相较于第一版的二分查找:
改动一:设置候选下标candidate,初始值设置为-1,-1代表没有找到的情况
改动二:如果说找到了对应的数组下标不返回,而是记录,并且向左移动J
注意一定是移动J——>m-1,如果是j=m,会死循环,这就可以导致这个candidate越来越往左靠
改动三:
下面就可以直接返回candidate!没有找到对应下标就返回-1,找到了最左边的对应数字下标就返回下标
在rightMost中:
相较于leftMost,这个移动的是第二处改动中的左标i:可以导致candidate越来越往右靠

九.代码简化,可用于查找某一范围的值,或者求排名

以leftmost为例:简化版leftmost版本
不返回candidate(去掉这个变量),而返回i:就是返回新数应该插入的地方:大于等于查找目标的最左边的数。也就是说,查找插入化为一体:缺点就是不会告诉你有没有那个值,需要你自己去调用该下标去对比(这个时候就有可能造成数组越界了)!
public class demo7 {
    public static void main(String[] args) {
        int target = 25;
        int[] arr = {0, 10, 20, 40, 40, 40, 40, 70, 80, 90};
        System.out.println(binarySearchRightMost2(arr, target));
    }
    private static int binarySearchLeftMost2(int[] arr, int target) {
        int i = 0, j = arr.length - 1;
        while (i <= j) {
            int m = (i + j)>>>1;
            int mid = arr[m];
            if (target <= mid) {
                //等于的情况也是向左移:因为要继续查找最左边的数
                //如果左移的j的下标不是目标值了也没关系,i会移动到正确的数字上,返回i即可
                j = m-1;
            }else{
                i = m+1;
            }
        }
        return i;
    }

    //返回大于等于目标值的最右边的数的下标(最后一步i+1一定会导致arr[i]大于目标值:所以返回值需要减一)
    //如果找不到目标值:插入的地方:应该是返回值+1
    private static int binarySearchRightMost2(int[] arr, int target) {
        int i = 0, j = arr.length - 1;
        while (i <= j) {
            int m = (i + j)>>>1;
            int mid = arr[m];
            if (target < mid) {
                j = m-1;
            } else{
                i = m+1;
            }
        }
        return i-1;
    }
}
此处的简化版leftmost中,这里的i可以用作查找排名,返回下标+1就是排名!

也可以可以使用此处的简化版leftmost与rightmost求范围!
比如1244477,可以找(注意下面的案例就算在数组中找不到也是对的:除非你越界数组了)
<4    0————leftMost-1
<=4   0————rightMost
>4    rightMost+1————无穷大
>=4   leftMost————无穷大


 

十.力扣题目实战经验分享

1.循环条件:尽量把i写前面,j写后面,用小于号,思维清晰一点

2.无符号右移把i+j用括号括起来

3.ifelse中的语句记得也尽量使用小于号,更加形象。

35. 搜索插入位置:三种解法
public class demo8 {
    public static void main(String[] args) {
        int[] nums={-1,0,3,5,9,12};
        System.out.println(searchInsert2(nums,6));
    }
    public static int searchInsert(int[] nums, int target) {
        int i=0, j=nums.length-1;
        while(i<=j){
            int m=(i+j)>>>1;
            int number=nums[m];
            if(target<number) {
                j=m-1;
            }else if(number<target){
                i=m+1;
            }else{
                return m;
            }
        }
        return i;
    }
    public static int searchInsert1(int[] nums, int target) {
        int i = Arrays.binarySearch(nums, target);
        return i>=0?i:-i-1;
    }
    public static int searchInsert2(int[] nums, int target) {
        //能够解决有重复元素的情况:找最左边的目标值
        int i = 0,j=nums.length-1;
        while (i<=j){
            int m = (i+j)>>>1;
            if (target<=nums[m]){
                j=m-1;
            }else {
                i=m+1;
            }
        }
        return i;
    }
}

34. 在排序数组中查找元素的第一个和最后一个位置:三种解法,一步一步改进的

尽量多用标题八中的没简化版本的代码

public class demo9 {
    public static void main(String[] args) {
        int[] nums={-1,0,3,5,9,9,9,9,9,9,9,9,12};
        System.out.println(Arrays.toString(searchRange3(nums,9)));
    }
    public static int[] searchRange(int[] nums, int target) {
        int[] array ={-1,-1};
        //找最左边的值,看看有没有这个数,同时也能够判断是否存在。
        int i = 0,j = nums.length - 1;
        while (i <= j) {
            int mid = (i + j) >>> 1;
            if (target <=nums[mid]) {
                j = mid - 1;
            } else {
                i = mid + 1;
            }
        }
        if (nums.length==0||i>=nums.length|| nums[i]!=target){//数组越界:1.空数组2.找的数大于数组最后一个3.没找到
            return array;
        }
        array[0] = i;

        //找最右边的值
        i = 0;
        j = nums.length - 1;
        while (i <= j) {
            int mid = (i + j) >>> 1;
            if (target < nums[mid]) {
                j = mid - 1;
            } else {
                i = mid + 1;
            }
        }
        array[1] = i - 1;
        return array;
    }
    public static int[] searchRange2(int[] nums, int target) {
        //第二版:使用candidate简化判断:只要不是-1,那么在数组中就一定存在!
        int[] array ={-1,-1};
        int i = 0,j = nums.length - 1,candidate = -1;
        while (i <= j) {
            int mid = (i + j) >>> 1;
            if (target <nums[mid]) {
                j = mid - 1;
            } else if (nums[mid]<target){
                i = mid + 1;
            }else {
                candidate=mid;
                j = mid - 1;
            }
        }
        if (candidate==-1){
            return array;
        }
        array[0] = i;

        i = 0;
        j = nums.length - 1;
        while (i <= j) {
            int mid = (i + j) >>> 1;
            if (target < nums[mid]) {
                j = mid - 1;
            } else {
                i = mid + 1;
            }
        }
        array[1] = i - 1;
        return array;
    }
    public static int[] searchRange3(int[] nums, int target) {
        //第三版:合并leftMost与rightMost
        return new int[]{findMost(nums,target,true),findMost(nums,target,false)};
    }
    public static int findMost(int[] nums, int target,boolean flag){
        int i = 0,j = nums.length-1;
        int candidate = -1;
        while (i<=j){
            int mid = (i+j)>>>1;
            int number = nums[mid];
            if (target<number){
                j = mid- 1;
            }else if (number<target){
                i= mid+1;
            }else{
                candidate=mid;
                if (flag){
                    j = mid -1;
                }else {
                    i = mid +1;
                }
            }
        }
        return candidate;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值