leetcode刷题--算法入门第一天--二分查找

二分查找

原理

二分查找算法的原理如下:

     1. 设置查找区间:low = 0;high= n;
     2. 若查找区间[low, high]不存在,则查找失败;否则转步骤3
     3. 取中间位mid = (low + high) / 2;比较 target 与 arr[mid],有以下三种情况:
       3.1 若 target < arr[mid],则high = mid - 1;查找在左半区间进行,转步骤2;
       3.2 若 target > arr[mid],则low = mid + 1;查找在右半区间进行,转步骤2;

例题

例一

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
基本和最原始的情况相同,简单的把算法原理转化为代码

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

例二

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
示例 1:

  输入:n = 5, bad = 4
  输出:4
  解释:
  调用 isBadVersion(3) -> false 
  调用 isBadVersion(5) -> true 
  调用 isBadVersion(4) -> true
  所以,4 是第一个错误的版本。
 来源:力扣(LeetCode)
 链接:https://leetcode.cn/problems/first-bad-version
 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

此题的情况是无法通过大小的比较来缩小的区间的范围,我的想法是利用mid左右元素的是否一致来判别是否找到第一个错误元素

  • 若左右元素不同

若mid为错误版本,则mid为第一个错误版本
若mid为正确版本,则mid+1为第一个错误版本

  • 若左右元素相同

若mid为错误版本,则第一个错误版本在左侧,令right=mid-1
mid为正确版本,则第一个错误版本在右侧,令left=mid+1

代码如下:

class Solution {
public:
    int firstBadVersion(int n) {
        long left=1;
        long right=n;
        bool p,q,r;
        while(left<=right)
        {
            long mid=(left+right)/2;
            p=isBadVersion(mid-1);//用p,q,r分别记录isBadVersion(mid-1),isBadVersion(mid),isBadVersion(mid+1)的情况,避免重复调用
            q=isBadVersion(mid);
            r=isBadVersion(mid+1);
            if(p!=r)
            {
                if(q) return mid;
                else if(!q) return mid+1;
            }
            else{
                if(!q) left=mid+1;
                else right=mid-1;
            }
        }
        return (right+left)/2;
    }
};
后来看过官方题解后发现,只需要不断的区间范围,即确定第一个错误版本的在那一边既可以不断的缩小区间直到将区间缩小到只剩一个元素,此时该元素即为第一个错误版本。
class Solution {
public:
    int firstBadVersion(int n) {
        int left = 1, right = n;
        while (left < right) { // 循环直至区间左右端点相同
            int mid = left + (right - left) / 2; // 防止计算时溢出
            if (isBadVersion(mid)) {
                right = mid; // 答案在区间 [left, mid] 中
            } else {
                left = mid + 1; // 答案在区间 [mid+1, right] 中
            }
        }
        // 此时有 left == right,区间缩为一个点,即为答案
        return left;
    }
};
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/first-bad-version/solution/di-yi-ge-cuo-wu-de-ban-ben-by-leetcode-s-pf8h/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

例三

搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/search-insert-position
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

主要思路和例二相同,类似二分插入的思路,即不断的缩小区间范围,若区间长度等于1或区间长度为0(left>right),即返回区间的最左处的值,此时可以确定位置

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left=0;
        int right=nums.size()-1;
        while(left<=right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]<target) left=mid+1;
            else right=mid-1;//只要不相等,就让right=mid-1,这样当left=right时匹配成功,left的值就是此时mid的值,若匹配失败,则left的值就该为应该插入的位置
       }
       return left;
    }
};
posted @ 2023-01-10 21:07  比奇堡的黄色小海绵  阅读(21)  评论(0)    收藏  举报