二分查找/折半查找模板

二分查找模板

快速了解一下二分查找

二分法,又称折半查找,时间复杂度O(logn)的查找算法,前提是有序数列,最基本的应用就是“猜数字”(一次缩小一半的范围,直到猜中数字),左闭右闭区间[l,r],它的拓展也与“猜数字”相似,变成了搜索大于等于target的第一个数或小于等于target的最后一个数(也就是最接近的数)

查找已存在的数

public static int findOne(int[] array,int target){
    int l = 0;
    int r = array.length - 1;
    while(l <= r){
        int mid = l + (r - l) / 2;//防止l+r超出int的存储大小,下同
        if(array[mid] < target) l = mid + 1;
        else if(array[mid] > target) r = mid - 1;
        else return mid;
    }
    return -1;
}

查找大于等于target的第一个数

public static int findFirstGreater(int[] array,int target){
    if(array.length == 0) return -1;
    int l = 0;
    int r = array.length - 1;
    while(l < r){
        int mid = l + (r - l) / 2;
        if(array[mid] < target) l = mid + 1;
        else r = mid;//找到符合的数不要立即返回,继续缩小区间,right = mid
    }
    if(l==array.length-1 && array[l]<target){
        return -1;// 所有的数都比target小,这时找不到答案
    }
    // 所有的数都比target大的情况l不会被mid+1赋值,即初始值0
    return l >= array.length ? -1 : l;
}

查找小于等于target的最后一个数

public static int findLastLess(int[] array,int target){
    if(array.length == 0) return -1;
    int l = 0;
    int r = array.length - 1;
    while(l < r){
        int mid = l + (r - l) / 2;
        if(array[mid] <= target) l = mid + 1;
        else r = mid;
    }
    if(r==array.length-1 && array[r]<=target){
        return r;// 所有的数都小于或者等于target,这个时候找到的是最后一个数,不用减一
    }
    // r-1处理了所有的数都比target大的情况
    return r - 1;//此时的r为大于target的第一个数
}

验证

  • 生成随机数据
// size是生成的列表大小,numBound是元素范围大小
public static int[] getSortedRandomList(int size,int numBound){
    int[] nums = new int[size];
    Random random = new Random();
    for(int i=0;i<size;i++){
        nums[i] = random.nextInt(numBound);
    }
    Arrays.sort(nums);
    return nums;
}
  • 结果生成函数
// condition等于0返回eq,大于0返回firstGreater,小于0返回lastLess
public static int check(int[] list,int target,int condition){
    int firstGreater=-1;
    int lastLess=-1;
    int eq = -1;
    for (int num : list) {
        if (num <= target) {
            lastLess = num;
        }
        if(num == target){
            eq = num;
        }
        if (num >= target) {
            firstGreater = num;
            break;
        }
    }
    return condition==0?eq:(condition>0?firstGreater:lastLess);
}
  • 完整代码
import java.util.*;

public class Main {
    public static int[] getSortedRandomList(int size,int numBound){
        int[] nums = new int[size];
        Random random = new Random();
        for(int i=0;i<size;i++){
            nums[i] = random.nextInt(numBound);
        }
        Arrays.sort(nums);
        return nums;
    }
    public static int check(int[] list,int target,int condition){
        int firstGreater=-1;
        int lastLess=-1;
        int eq = -1;
        for (int num : list) {
            if (num <= target) {
                lastLess = num;
            }
            if(num == target){
                eq = num;
            }
            if (num >= target) {
                firstGreater = num;
                break;
            }
        }
        return condition==0?eq:(condition>0?firstGreater:lastLess);
    }
    public static int findFirstGreater(int[] array,int target){
        if(array.length == 0) return -1;
        int l = 0;
        int r = array.length - 1;
        while(l < r){
            int mid = l + (r - l) / 2;
            if(array[mid] < target) l = mid + 1;
            else r = mid;//找到符合的数不要立即返回,继续缩小区间,right = mid
        }
        if(l==array.length-1 && array[l]<target){
            return -1;// 所有的数都比target小,这时找不到答案
        }
        // 所有的数都比target大的情况l不会被mid+1赋值,即初始值0
        return l >= array.length ? -1 : l;
    }
    public static int findLastLess(int[] array,int target){
        if(array.length == 0) return -1;
        int l = 0;
        int r = array.length - 1;
        while(l < r){
            int mid = l + (r - l) / 2;
            if(array[mid] <= target) l = mid + 1;
            else r = mid;
        }
        if(r==array.length-1 && array[r]<=target){
            return r;// 所有的数都小于或者等于target,这个时候找到的是最后一个数,不用减一
        }
        // r-1处理了所有的数都比target大的情况
        return r - 1;//此时的r为大于target的第一个数
    }
    public static void main(String[] args) {
        Random random = new Random();
        for(int i=0;i<100;i++){
            int flag=0;
            int target = random.nextInt(1000);
            int[] list = getSortedRandomList(100,1000);
            int greaterIndex = findFirstGreater(list,target);
            int lessIndex = findLastLess(list,target);
            int greater = greaterIndex==-1?-1:list[greaterIndex];
            int less = lessIndex==-1?-1:list[lessIndex];
            int greaterAns = check(list,target,1);
            int lessAns = check(list,target,-1);
            if(greater!=greaterAns){
                flag=1;
                System.out.println("---------------greater error--------------");
                System.out.println(Arrays.toString(list));
                System.out.println("target="+target);
                System.out.println("greater="+greater+" greaterAns="+greaterAns);
                System.out.println("------------------------------------------");
            }
            if(less!=lessAns){
                flag=1;
                System.out.println("----------------less error----------------");
                System.out.println(Arrays.toString(list));
                System.out.println("target="+target);
                System.out.println("less="+less+" lessAns="+lessAns);
                System.out.println("------------------------------------------");
            }
            if(flag==0){
                System.out.println("[success] target="+target+" greater="+greater+" less="+less);
            }
        }

    }
}

关于测试

以上的二分查找模板经过我自己测试,可以满足99%的情况(其实我觉得差不多够用了,懒得用数学证明到底是不是100%)

我把我自己编写的测试函数写在上面了,有需要可以自己测一测,下面是我测试的一些情况。

  • 奇/偶数个随机数值(1-1000)的元素
  • 空列表
  • 列表中存在/不存在target
  • 元素全部大于target
  • 元素全部小于target
  • 元素全部大于或者等于target
  • 元素全部小于或者等于target

练手的题目(leetcode)

34. 在排序数组中查找元素的第一个和最后一个位置

35. 搜索插入位置

167. 两数之和 II - 输入有序数组

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fan_11235813

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值