关于二分查找的如何确定while条件

667 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

二分查找背了模板之后发现很多时候并不能灵活的使用到题中。因此本文针对几种情况讨论如果确定条件

题目描述

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

这是最简单的二分了

int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size();
        while(left < right){
            int mid = (left + right ) / 2;
            if(nums[mid] > target){
                right = mid;
            }else if (nums[mid] < target){
                left = mid;
            }else {
                return mid;
            }
        }
        return nums[left] == target?left:-1;
    }

这么一写,就发现超时了,那怎么会超时呢?

思路分析

这类问题是因为mid很可能在left = 2n,right=2n+1时仍然等于2n,这样的话很可能造成死循环,回归题意,我们使用二分其实是想确定一个范围为1的区间,当这个区间里元素等于target时,则认为找到了,而对于(nums[mid] > target),其实是不需要再判断mid的,因此可以调整为right-1。

这只是一个开始

题目再描述

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

L1T.png

问题关键在于,此刻我们不能在nums[mid] = target的时候返回,而是要将其设为right = mid,然而在这个时候又出现了死循环的隐患。

我们不妨换个思路,还是只找一个值但是找的是target开始位置前一个值,以及开始位置后一个值

 while (left < right) {
    int mid = (right - left) / 2 + left;
    if (nums[mid] >= target) {
        right = mid - 1;
    }else {
        left = mid + 1;
    }
}

这个时候代码会类似这个样子,此时想找到这个值只需要再修正一次即可,即对最后的结果根据需要进行加一或减一,当然也可以直接修改为while (left <= right) {