基础数据结构——二分查找进阶(新手必看!)

引言:

在之前学习的二分查找中,我们全部是按照没有重复元素的顺序查找二分

那在处理有重复元素的时候我们又该如何进行查找呢

我们回到之前写过的二分查找基础版

public  static int binarysearchbasic2(int[] a,int target){
        int i=0,j=a.length-1;//设置初始指针
        while (i<=j){
            int m=(i+j)>>>1;
            if (a[m]<target){
                i=m+1;
            }else if(target<a[m]){
                j=m-1;
            }else {
                return m;
            }
        }
        return -1;
    }

我们不难发现,当该函数查找到第一个待查找元素的时候就会返回该元素,如果在查找一个含有重复元素的数组,其返回的也是第一个查找到的元素所在的索引。

我们能不能改进一下需求呢 比如返回重复元素中最左侧的元素

查找重复元素中最左侧的二分查找(leftmost)

我们先来看思路(蓝色为数组,紫色为数组索引值

我们选择的目标值为4

在上述数组中,我们进行一次二分查找,成功标记到了4,我们将其进行标记,并且将查找范围的末尾指针移到标记点前一位

并完成下一次二分,同时因为此时标记的m小于4,我们将左指针移到m的右边

并找到其对应的m

此时查找到的m为更靠近左边的待查找值的索引,所以我们重新标记,并将j移到m-1,查找是否存在更近的元素

查找到j<i的时候,退出循环,标记的m即为最靠左侧的索引。

代码实现

我们只需要在对应的二分查找基础版的基础上去处理“a[m]=target”的情况即可

 public static int binarysearchleftmmost1(int[]a,int target){
        int i=0,j=a.length-1;
        int candidate=-1;
        while (i<=j){
            int m=(i+j)>>>1;
            if(target<a[m]){
                j=m-1;
            }else if (a[m]<target){
                i=m+1;
            }else {
                candidate=m;//标记该位置
                j=m-1;///查找到了待查找的元素,将末尾指针提前去查找左侧是否含有
            }
        }
        return candidate;
    }

查找重复元素中最右侧的二分查找(rightmost)

同理,我们可以得出查找重复元素最右侧的二分查找(和Leftmost不停=同的是查找到元素之后改为向右查找)

public static int binarysearchrightmmost1(int[]a,int target){
        int i=0,j=a.length-1;
        int candidate=-1;
        while (i<=j){
            int m=(i+j)>>>1;
            if(target<a[m]){
                j=m-1;
            }else if (a[m]<target){
                i=m+1;
            }else {
                candidate=m;//标记该位置
                i=m+1;///查找到了待查找的元素,将初始指针后移去查找右侧是否含有
            }
        }
        return candidate;
    }

我们对于上述代码更进一步思考

在上述两个方法中,我们没有查找到的返回值都是-1,我们接着改进代码去使其返回值更加有用

我们将上述方法的返回值改成i,此时之前代码中的candidate没用了,可以进行删除,我们进而观察优化过的代码

以下是改进过的leftmost代码

public static int binarysearchleftmmost2(int[]a,int target){
        int i=0,j=a.length-1;
        int candidate=-1;
        while (i<=j){
            int m=(i+j)>>>1;
            if(target<=a[m]){
                j=m-1;
            }else{
                i=m+1;
            }
        }
        return i;
    }

我们对于该代码进行运行

查找值为4时,返回值为4

当查找到该元素的时候,返回的是数组中重复元素的最左边元素

查找值为5时,返回值为7

当查找不到该元素时,返回的是数组中比目标大的最靠左的值

接下来我们做一个总结

二分查找Leftmost

public static int binarysearchleftmmost2(int[]a,int target){
        int i=0,j=a.length-1;
        int candidate=-1;
        while (i<=j){
            int m=(i+j)>>>1;
            if(target<=a[m]){
                j=m-1;
            }else{
                i=m+1;
            }
        }
        return i;
    }

返回值为>=target的最左侧索引

二分查找rightmost(同理)

 public static int binarysearchrightmmost2(int[]a,int target){
        int i=0,j=a.length-1;
        while (i<=j){
            int m=(i+j)>>>1;
            if(target<a[m]){
                j=m-1;
            }else{
                i=m+1;
            }
        }
        return i-1;
    }

返回值为<=target的最靠右索引

问题1:

在rightmost中为什么返回值要是i-1,我们看下图 target值为5 

下列的值里面j或者i-1代表的是<=target的最靠右索引

那在我们学习完Leftmost,Rightmost之后这些有什么用呢

我们接着讲

我们先引入这个概念

前任 后任不难理解,即为target的前一个后一个

最近邻居的意思是前后任里面数值差距最小的

求排名的时候,可以通过Leftmost找到

比如我们求4的排名,我们得到返回值2(索引) 加上一即为4的排名

比如我们求5的排名 我们得到返回值5 索引值+1即为排名

求前任的索引

求后任的索引

接下来我们直接上所有范围

以上学习笔记参考黑马程序员,大家有兴趣深入学习可以去学习他们的教材,谢谢大家

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值