二分查找----java--黑马

二分查找

一、基础版

使用双指针

public static int binarySearch(int[] a, int target) {
    int i = 0;
    int j = a.length - 1;
    while (i <= j) {
        int p = (i + j) >>> 1;
        if (target < a[p]) {
            j = p - 1;
        } else if (target > a[p]) {
            i = p + 1;
        } else {
            // 找到目标值索引位置
            return p;
        }
    }
    // 未找到目标值索引,则返回-1
    return -1;
}

二分查找算法性能

时间复杂度

  • 最坏情况: O ( log ⁡ n ) O(\log n) O(logn)
  • 最好情况:如果带查找目标值正好位于中间位置,则循环一次, O ( 1 ) O(1) O(1)

空间复杂度

  • 需要常数个指针 i , j , p i, j, p i,j,p ,因此空间复杂度为 O ( 1 ) O(1) O(1)

二、平衡版

public static int binarySearch(int[] a, int target) {
    int i = 0;
    int j = a.length;
    while (1 < j - i) {
        int p = (i + j) >>> 1;
        if (target < a[p]) {
            j = p;
        } else {
            i = p;
        }
    }
    if (a[i] == target) {
        return i;
    } else {
        return -1;
    }
}
  • 左闭右开区间, i i i 可能是目标值,而 j j j 指向的不是目标值;

  • 不在循环内找出目标值,只在循环结束,比较最后 i i i 指向的值与目标值,即, a [ i ] = = t a r g e t a[i] == target a[i]==target 是否为 t r u e true true ;

  • 循环内的比较次数减少;

  • 时间复杂度 O ( log ⁡ n ) O(\log n) O(logn) (最好和最坏情况)

三、java版本

public static int binarySearch(int[] a, int target) {
    int low = 0;
    int high = a.length - 1;
    while (low <= high) {
        int mid = (low + high) >>> 1;
        if (a[mid] > target) {
            j = mid - 1;
        } else if (a[mid] < target) {
            i = mid + 1;
        } else {
            return mid;
        }
    }
    return -(low + 1);  // 返回-(insertion point + 1) 另外,找不到不能返回0,因为0无法判断是找到,还是未找到
}

四、二分查找 leftMost & rightMost 版本

l e f t M o s t leftMost leftMost 版本

public static int binarySearchLeftMost(int[] a, int target) {
    int i = 0;
    int j = a.length - 1;
    int candidate = -1;
    while (i <= j) {
        int p = (i + j) >>> 1;
        if (target < a[p]) {
            j = p - 1;
        } else if (target > a[p]) {
            i = p + 1;
        } else {
            // 找到目标值索引位置,但不能确定是目标值最左侧索引
            candidate = p;
            j = p - 1;
        }
    }
    // 未找到目标值索引,则返回-1
    return candidate;
}

改进版本

public static int binarySearchLeftMost(int[] a, int target) {
    int i = 0;
    int j = a.length - 1;
    int candidate = -1;
    while (i <= j) {
        int p = (i + j) >>> 1;
        if (target <= a[p]) {
            j = p - 1;
        } else {
            i = p + 1;
        }
    }
    // 返回大于或等于目标值得最左侧索引位置
    return i;
}

return : 返回 ≥ t a r g e t \ge target target 的最左侧索引位置

r i g h t M o s t rightMost rightMost 版本

public static int binarySearchRightMost(int[] a, int target) {
    int i = 0;
    int j = a.length - 1;
    int candidate = -1;
    while (i <= j) {
        int p = (i + j) >>> 1;
        if (target < a[p]) {
            j = p - 1;
        } else if (target > a[p]) {
            i = p + 1;
        } else {
            // 找到目标值索引位置,不能确定是目标值最右侧索引
            candidate = p;
            i = p + 1;
        }
    }
    // 未找到目标值索引,则返回-1
    return candidate;
}

改进版本

public static int binarySearchRightMost(int[] a, int target) {
    int i = 0;
    int j = a.length - 1;
    int candidate = -1;
    while (i <= j) {
        int p = (i + j) >>> 1;
        if (target < a[p]) {
            j = p - 1;
        } else {
            i = p + 1;
        } 
    }
    // 返回小或等于目标值的最右侧索引位置 
    return i - 1;
}

return : 返回 ≤ t a r g e t \le target target 的最右侧索引位置

五、二分查找应用

在这里插入图片描述

5.1 求排名

使用 l e f t m o s t leftmost leftmost 版本

假如,求 4 4 4 的排名, l e f t m o s t ( a , 4 ) + 1 leftmost(a, 4) + 1 leftmost(a,4)+1 l e f t m o s t ( a , 4 ) leftmost(a, 4) leftmost(a,4) 得到索引位置为2(该索引位置值 a [ 2 ] = 4 a[2] = 4 a[2]=4 ),因为数组索引从0开始,排名则需要加一。

假如,求 5 5 5 的排名, l e f t m o s t ( a , 5 ) + 1 leftmost(a, 5)+1 leftmost(a,5)+1 l e f t m o s t ( a , 5 ) leftmost(a, 5) leftmost(a,5) 得到大于等于目标值 5 5 5 最左侧索引 5 5 5 (该索引位置值 a [ 5 ] = 7 a[5] = 7 a[5]=7 ),然后加一操作。

5.2 求前任

使用 l e f t m o s t leftmost leftmost 版本

假如,求 4 4 4的前任, l e f t m o s t ( a , 4 ) − 1 leftmost(a, 4)-1 leftmost(a,4)1 l e f t m o s t ( a , 4 ) leftmost(a, 4) leftmost(a,4) 得到大于等于目标值 4 4 4 最左侧索引位置2(该索引位置值 a [ 2 ] = 4 a[2] = 4 a[2]=4 ),因为前任则需要减一操作。

假如,求 5 5 5的前任, l e f t m o s t ( a , 5 ) − 1 leftmost(a, 5)-1 leftmost(a,5)1 l e f t m o s t ( a , 5 ) leftmost(a, 5) leftmost(a,5) 得到大于等于目标值 5 5 5 最左侧索引位置5(该索引位置值 a [ 5 ] = 7 a[5] = 7 a[5]=7 ),因为前任则需要减一操作。

5.3 求后任

使用 r i g h t m o s t rightmost rightmost 版本

假如,求 4 4 4 的后任, r i g h t m o s t ( a , 4 ) + 1 rightmost(a, 4)+1 rightmost(a,4)+1 r i g h t m o s t ( a , 4 ) rightmost(a, 4) rightmost(a,4) 得到小于等于目标值 4 4 4 最右侧索引位置4(该索引位置值 a [ 4 ] = 4 a[4] = 4 a[4]=4 ),因为后任则需要加一操作。

假如,求 5 5 5 的后任, r i g h t m o s t ( a , 5 ) + 1 rightmost(a, 5)+1 rightmost(a,5)+1 r i g h t m o s t ( a , 5 ) rightmost(a, 5) rightmost(a,5)得到小于等于目标值 5 5 5 最右侧索引位置4(该索引位置值 a [ 4 ] = 4 a[4] = 4 a[4]=4 ),因为后任则需要加一操作。

5.4 最近邻居

寻找前任和后任中最小的,即为最小邻居。

假如求 4 4 4 的最近邻居,使用 l e f t m o s t leftmost leftmost 求前任,找到大于等于 4 4 4 最左侧索引位置2,然后减一操作,即 l e f t m o s t ( a , 4 ) − 1 leftmost(a, 4) - 1 leftmost(a,4)1 ;

使用 r i g h t m o s t rightmost rightmost 求后任,找打小于等于 4 4 4 的最右侧索引位置4,然后加以操作,即 r i g h t m o s t ( a , 4 ) + 1 rightmost(a, 4) + 1 rightmost(a,4)+1 ;

最后在前任与后任之间取最小值,即 M a t h . m i n ( a [ l e f t m o s t ( a , 4 ) − 1 ] , a [ r i g h t m o s t ( a , 4 ) + 1 ] ) Math.min(a[leftmost(a, 4)-1], a[rightmost(a, 4)+1]) Math.min(a[leftmost(a,4)1],a[rightmost(a,4)+1])

值应为2

5.5 寻找区间

< 4 <4 <4 的区间,使用 l e f t m o s t leftmost leftmost ,找到大于等于 4 4 4 最左侧索引位置,然后减一,区间则为 [ 0 , l e f t m o s t ( a , 4 ) − 1 ] [0, leftmost(a, 4)-1] [0,leftmost(a,4)1]
在这里插入图片描述

≤ 4 \le4 4 的区间,使用 r i g h t m o s t rightmost rightmost ,找到小于等 4 4 4 最右侧索引位置,区间即为 [ 0 , r i g h t m o s t ( a , 4 ) ] [0, rightmost(a, 4)] [0,rightmost(a,4)]
在这里插入图片描述

> 4 >4 >4 的区间,使用 r i g h t m o s t rightmost rightmost ,找到小于等于 4 4 4 的最右侧索引位置 4 4 4 , 区间为 [ r i g h t m o s t ( a , 4 ) + 1 , a . l e n g t h ) [rightmost(a, 4)+1, a.length) [rightmost(a,4)+1,a.length)
在这里插入图片描述
4 < x < 7 4<x<7 4<x<7 的区间,使用 r i g h t m o s t rightmost rightmost 找到小于等于4最右侧索引位置,然后加一,使用 l e f t m o s t leftmost leftmost 找到大于等于7最左侧索引位置,然后减一,即, [ l e f t m o s t ( a , 4 ) + 1 , r i g h t m o s t ( a , 7 ) − 1 ] [leftmost(a, 4) + 1, rightmost(a, 7) - 1] [leftmost(a,4)+1,rightmost(a,7)1]

4 ≤ x ≤ 7 4\le x\le7 4x7 的区间,使用 l e f t m o s t leftmost leftmost 找到大于等于4最左侧索引位置,使用 r i g h t m o s t rightmost rightmost找到小于等于7的最右侧索引位置,即 [ l e f t m o s t ( a , 4 ) , r i g h t m o s t ( a , 7 ) ] [leftmost(a, 4), rightmost(a, 7)] [leftmost(a,4),rightmost(a,7)]
在这里插入图片描述

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值