【LeetCode-查找】二分查找

题目描述

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

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

注意:

  • 你可以假设 nums 中的所有元素是不重复的。
  • n 将在 [1, 10000]之间。
  • nums 的每个元素都将在 [-9999, 9999]之间。

题目链接: https://leetcode-cn.com/problems/binary-search/

思路

经典的二分查找问题。二分查找的难点在于不容易写对,也就是不容易判断应该使用 < 还是 <=,还有要不要 +1,-1 的问题。二分查找主要有两种写法:

写法一

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(nums.empty()) return -1;

        int left = 0, right = nums.size()-1;
        while(left<=right){
            int mid = left + (right-left)/2;    // 防止溢出
            if(nums[mid]==target) return mid;
            else if(nums[mid]>target){
                right = mid-1;
            }
            else if(nums[mid]<target){
                left = mid+1;
            }
        }
        return -1;
    }
};

写法二

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(nums.empty()) return -1;

        int left = 0, right = nums.size();
        while(left<right){
            int mid = left + (right-left)/2;    // 防止溢出
            if(nums[mid]==target) return mid;
            else if(nums[mid]>target){
                right = mid;
            }
            else if(nums[mid]<target){
                left = mid+1;
            }
        }
        return -1;
    }
};

两种写法的不同体现在3个方面,如下图:

3处不同分别是:

  • right的初始化;
  • while循环条件;
  • right的更新方式;

3处不同的原因取决于我们的搜索范围是[left, right]还是[left, right)。下面分析两种不同写法的区别以及原因:

  • 对于写法1,right初始化为nums.size()-1,意味着搜索的范围为[left, right],因为nums.size()-1指向nums的最后一个元素,如果搜索范围是[left, right)的话,最后一个数字就不在搜索范围了。在搜索范围为[left, right]的情况下,left==right也就是[left, left]这样的范围是有意义的,例如[2,2]意味着对下标为2的数字进行搜索,所以while的条件为left<=right。在搜索的过程中,我们根据mid指向的元素与target之间的大小关系来选择接下来的搜索范围,因为搜索范围是[left, right],所以在更新时right要更新为mid-1,如果不减一的话,下一步搜索的范围就包含mid了。

  • 对于写法2,right初始化为nums.size(),意味着搜索范围为[left, right),因为right初始指向num.size(),也就是nums最后一个数字的下一个位置,[left, right)不包含right指向的元素,刚好搜索范围是整个数组。在搜索范围是[left, right)的情况下,left==right也就是[left, left)这样的条件是无意义的,所以while的条件为left<right。因为搜索范围是左闭右开,不包含right指向的元素,所以right=mid即可,无需更新为mid-1,因为mid不会被包含进搜索范围中。

参考

1、https://leetcode-cn.com/problems/binary-search/solution/er-fen-cha-zhao-de-xun-huan-bu-bian-liang-zhi-yao-/

posted @ 2020-04-29 16:35  Flix  阅读(303)  评论(0编辑  收藏  举报