二分查找

二分查找

​ 初始查找范围在整个数组 [left,right] ,每次取查找范围的中点 mid ,比较 num[mid] 和target ,如果 它俩相等,则 mid 就是要找的下标;如果不相等则根据它们关系将查找范围缩小一半

1、使用条件:

  • 有序数组;
  • 该数组中无重复元素

2、问题

给定一有序整型数组 nums[n] 和一个目标值 target ,这里假设是升序;

要求:搜索nums 中的 target ,如果目标值存在,则返回下标,否则返回 -1。
题目链接:https://leetcode-cn.com/problems/binary-search

输入: 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

3、分析

数组有序,无重复元素,一旦出现重复元素,返回下标可能不唯一;

写二分法,区间的定义一般为两种,左闭右闭即[left, right],

或者左闭右开即[left, right)。

4、代码

//法一: [left,right]
    public  int search(int[] nums,int target){
        // 考虑target 超出搜索数组范围
        if (target < nums[0] || target > nums[nums.length-1]){
            return -1;
        }
        // 当数组有n 个元素,不知道要执行多少次,想到用while
        // 定义target在左闭右闭的区间里,[left, right]:
        int left = 0;
        int right = nums.length - 1;

        while (left <= right){ 
            int mid = left + (right - left)/2;
            // 防止溢出 等同于(left + right)/2
            if (nums[mid] == target){
                return mid;
            }else if (nums[mid] < target){
                // 中间值在目标值的左边,
                left = mid + 1;
                // 缩小区间为[mid + 1,right]
            }else if (nums[mid] > target){
                // 中间值在目标值的右边,
                right = mid - 1;
                // 缩小区间为[left,mid-1]
            }
        }
        // 没找到target
        return -1;

//法二: [left,right)
        public  int search(int[] nums,int target){
            // 定义target在左闭右开的区间里,[left, right):
            int left = 0;
            int right = nums.length; /* 和法一的区别 */

            while (left < right){
                int mid = left + (right - left)/2;
                if (nums[mid] == target){
                    return mid;
                }else if (nums[mid] < target){
                    // 中间值在目标值的左边,
                    left = mid + 1;
                    // 缩小区间为[mid+1,right)
                }else if (nums[mid] > target){
                    // 中间值在目标值的右边,
                    right = mid; /* 这里和法一的区别 */
                    // 缩小区间为[left,mid)
                }
            }
            // 没找到target
            return -1;
        }

5、总结

这里有几点需要注意:

1、target 的两个定义区间的范围不同:

  • (target范围和数组范围不一样!!!)
  • 当在左闭右闭时,[left,right] = [0,num.length-1]
  • 在左闭右开时,[left,right) = [0,num.length)

2、注意不同区间时循环条件:(为什么要考虑两个区间呢???)

  • [left,right] 时,while ( left <= right )
  • [left,right) 时,while ( left < right )

3、注意两区间下,

当 nums[mid] > target 时,

  • 在 [left,right] ,搜索区间变为 [ left,mid-1]
  • 在 [left,right),搜索区间变为 [ left,mid)
posted @ 2022-07-15 17:49  来日可追  阅读(40)  评论(0编辑  收藏  举报