【二分查找模板】

概要

四种题型方法对比图片

1、查目标值下标

    public int search(int[] nums, int target) {
        int left=0;
		int right = nums.length-1;
		while(left<=right) {    //要有等于,因为left和right和mid有相等的时候,不能少
			int mid=left+(right-left)/2;
			if(nums[mid]>target) {
				right=mid-1;    //若在左边区间,更新右区间,mid不是了,往前,右区间减1
			}else if(nums[mid]<target) {
				left = mid+1;    //若在右边区间,更新左区间,mid不是了,往后找,左区间+1
			}else{
				return mid;      //若mid是输出mid
            }
		}
		return -1;
    }

2 、搜索插入位置

    public int search(int[] nums, int target) {
        int left=0;
		int right = nums.length-1;
		while(left<=right) {    //要有等于,因为left和right和mid有相等的时候,不能少
			int mid=left+(right-left)/2;
			if(nums[mid]>target) {
				right=mid-1;    //若在左边区间,更新右区间,mid不是了,往前,右区间减1
			}else if(nums[mid]<target) {
				left = mid+1;    //若在右边区间,更新左区间,mid不是了,往后找,左区间+1
			}else{
				return mid;      //若mid是输出mid
            }
		}
		return mid;
    }

3、找右边界

	public static int getRightBorder(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int rightBorder = -1; // 记录一下rightBorder没有被赋值的情况
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] > target) {
                right = middle - 1;
            } else if (nums[middle] < target) { 
                left = middle + 1;
            }else {
            	left = middle + 1; // 因为要找右边界,先找到了一个,先别急,更新left往后再找找看还有没有相等的。left变大了,mid也变大
                rightBorder = middle;  //找到了就先把这个右边界给存起来
            }
        }
        return rightBorder;
    }

4、找左边界

	public static int getLeftBorder(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int leftBorder = -1; // 记录一下leftBorder没有被赋值的情况
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] > target) {
                right = middle - 1;
            } else if (nums[middle] < target) {
                left = middle + 1;
            }else {
            	right = middle - 1;   // 因为要找左边界,先找到了一个,先别急,更新right往前再找找看还有没有相等的
                leftBorder = middle;  //找到了就先把这个左边界给存起来
            }
        }
        return leftBorder ;
    }

5、找第一个大于等于目标值的下标

    public static int getLeftBorder(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int leftBorder = -1; // 记录一下leftBorder没有被赋值的情况
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] > target) { // 寻找左边界,nums[middle] == target的时候更新right
                right = middle - 1;
            } else if (nums[middle] < target) {
                left = middle + 1;
            } else {
                right = middle - 1;
                leftBorder = middle;
            }
        }
        if (leftBorder == -1) { //和找左边界不同的地方:如果没找到并且下标在数组内,应该返回第一个比目标值大的下标
            return left < nums.length ? left : -1;
        } else {
            return leftBorder;
        }

这里和找左边界不同之处是,返回值不同,找左边界没找到返回-1,这里没找到是要返回大于等于目标值的下标,left就是第一个比目标值的下标。left要是在数组内,就返回left,不在就是返回-1.

6、java自带的函数:Arrays类的binarySearch

(1)源码

    //两个参数,数组a,和要查找的值key
    public static int binarySearch(int[] a, int key) {
        return binarySearch0(a, 0, a.length, key);
    }
     private static int binarySearch0(int[] a, int fromIndex, int toIndex,int key) {
        int low = fromIndex;
        int high = toIndex - 1;

        while (low <= high) {
            int mid = (low + high) >>> 1;
            int midVal = a[mid];

            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }

这里方法的和方法一不一样的地方,是没找到后返回的值是bound: -(low + 1)。
返回结果大于0,就是表示,数组中等于目标值的下标,但注意假如数组中有重复的元素,返回并不是第一个等于的,[1,3,3,5,6],找3,输出的并不是下标1,是2,因为源码中,找到了一个大于等于的值就结束了,并没有接着更改left的值接着往左边找。当然有的时候会输出第一个下标,这要根据重复的元素在数组中的位置,看程序先找到哪一个,可以自己试一下。
返回结果小于0,表示数组没有找到该目标值,那么他应该插入的地方,即为第一个大于目标值的元素下标为**-bound-1**。例子:[1,3,3,5,6],找2,这里的第一个大于目标值的元素是指3,但是是哪个3不知道,而并不是指第一个3。
为什么是这个**-bound-1**,下面结合例子讲

(2)应用:当数组内无重复元素时

    public static void main(String[] args) {
		int[] arr = {1, 10, 23, 35, 55, 66, 88};
		//二分查找,binarySearch方法
		int index1 = Arrays.binarySearch(arr,66);
		int index2 = Arrays.binarySearch(arr,18);
		int index3 = Arrays.binarySearch(arr,-1);
		int index4 = Arrays.binarySearch(arr,99);
		System.out.println("66的索引值为:" + index1);
		System.out.println("18的索引值为:" + index2);
		System.out.println("-1的索引值为:" + index3);
		System.out.println("99的索引值为:" + index4);
	} 

输出结果

66的索引为:5
18的索引为:-3
-1的索引为:-1
99的索引为:-8
  • key不是数组内的数,但在这个有序数组的范围内:
    如在数组{1, 10, 23, 35, 55, 66, 88} 中找18一样:
    起始low = 0,high = 6。
    第一轮循环:mid = 3,midVal = a[3] = 35 > 18,high = mid - 1 = 2
    第二轮循环:mid = 1,midVal = a[1] = 10 < 18,low = mid + 1 = 2
    第三轮循环:mid = 2,midVal = a[2] = 23 > 18,high = mid - 1 = 1
    第四轮循环:此时low = 2 > high = 1,跳出while循环,返回 bound为- (low +1) = - 3
    那么就是第一个比目标值的大的元素下标为-bound-1:2
  • key不是数组内的数,且小于数组内最小的数:
    如在数组{1, 10, 23, 35, 55, 66, 88} 中找-1一样:
    由于key = -1 < 1,所以它的low会一直为0,而high和mid会不断向0靠近,最终high = -1时跳出循环,返回的索引就为:- (low +1) = - (0 +1) = -1,这也是为什么返回的是- (low +1).
    那么就是第一个比目标值的大的元素下标为-bound-1:0
  • key不是数组内的数,且大于数组内最大的数:
    如在数组{1, 10, 23, 35, 55, 66, 88} 中找99一样:
    由于key = 99 > 88,所以它的high会一直为a.length - 1,而low和mid会不断向靠近arr.length - 1,最终low = a.length时跳出循环,返回的索引就为:- (low +1) = - (a.length +1) = - 8
    那么就是第一个比目标值的大的元素下标为-bound-1:7

(3)应用:当数组内有重复元素时

这里的重复元素前面已经介绍过,当数组内有重复元素时,[1,3,3,5,6],找3,这个函数找到的这个元素并不是最左边的第一个,因为找到一个就停止了没接着往左边找。

(4)总结,和适用场景

1、假如数组没有重复元素,找到的是第一个下标
2、假如要找的是元素,而不是对应的下标,是否重复不影响

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值