二分查找(循序渐进由0到1掌握二分)

写在前面:博主是一位普普通通的19届双非软工在读生,平时最大的爱好就是听听歌,逛逛B站。博主很喜欢的一句话花开堪折直须折,莫待无花空折枝:博主的理解是头一次为人,就应该做自己想做的事,做自己不后悔的事,做自己以后不会留有遗憾的事,做自己觉得有意义的事,不浪费这大好的青春年华。博主写博客目的是记录所学到的知识并方便自己复习,在记录知识的同时获得部分浏览量,得到更多人的认可,满足小小的成就感,同时在写博客的途中结交更多志同道合的朋友,让自己在技术的路上并不孤单。

目录:
1.二分查找简介
2.基本二分搜索
       
基本二分搜索简介
       
LeetCode 69.x的平方根
       
LeetCode 374.猜数字大小
       
LeetCode 33.搜索旋转排序数组
3.寻找左侧边界的二分搜索
       
寻找左侧边界的二分搜索简介
       
LeetCode 278.第一个错误的版本
       
LeetCode 162.寻找峰值
       
LeetCode 153. 寻找旋转排序数组中的最小值
4.寻找右侧边界的二分查找
       
寻找右侧边界的二分查找简介
       
LeetCode34.在排序数组中查找元素的第一个和最后一个位置
       

1.二分查找

二分查找法(Binary Search)算法,也叫折半查找算法。二分查找针对的是一个有序(如果集合是无序的,我们可以总是在应用二分查找之前先对其进行排序。)的数据集合,查找思想有点类似于分治思想。每次都通过跟区间的中间元素对比,将带查找的区间缩小为之前的一半,知道找到要查找的元素,或者区间被缩小为0。二分查找是一种非常非常高效的查询算法,时间复杂度未O(logn)。

2.基本二分搜索

2.1基本二分搜索简介与代码模板

本分查找的最基础和最基本的形式。
查找条件可以在不与元素的两侧进行比较的情况下确定(或使用它周围的特定元素)。
不需要后处理,因为每一步中,你都在检查是否找到了元素。如果到达末尾,则知道未找到该元素。

int binarySearch(vector<int>& nums, int target){
   
  if(nums.size() == 0)
    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) 
     left = mid + 1;
    else
    right = mid - 1; 
  }
  return -1;
}

可能在基本二分搜索会遇到如下问题:

2.为什么 while 循环的条件中是 <=,而不是 <?

答:因为初始化 right 的赋值是 nums.length - 1,即最后一个元素的索引,而不是 nums.length。
这二者可能出现在不同功能的二分查找中,区别是:前者相当于两端都闭区间 [left, right],后者相当于左闭右开区间 [left, right),因为索引大小为 nums.length 是越界的。
我们这个算法中使用的是前者 [left, right] 两端都闭的区间。这个区间其实就是每次进行搜索的区间。
什么时候应该停止搜索呢?当然,找到了目标值的时候可以终止:

if(nums[mid] == target)
    return mid; 

但如果没找到,就需要 while 循环终止,然后返回 -1。那 while 循环什么时候应该终止?搜索区间为空的时候应该终止,意味着你没得找了,就等于没找到嘛。
while(left <= right) 的终止条件是 left == right + 1,写成区间的形式就是 [right + 1, right],或者带个具体的数字进去 [3, 2],可见这时候区间为空,因为没有数字既大于等于 3 又小于等于 2 的吧。所以这时候 while 循环终止是正确的,直接返回 -1 即可。
while(left < right) 的终止条件是 left == right,写成区间的形式就是 [left, right],或者带个具体的数字进去 [2, 2],这时候区间非空,还有一个数 2,但此时 while 循环终止了。也就是说这区间 [2, 2] 被漏掉了,索引 2 没有被搜索,如果这时候直接返回 -1 就是错误的。

2.此算法有什么缺陷?

比如说给你有序数组 nums = [1,2,2,2,3],target 为 2,此算法返回的索引是 2,没错。但是如果我想得到 target 的左侧边界,即索引 1,或者我想得到 target 的右侧边界,即索引 3,这样的话此算法是无法处理的。
这样的需求很常见,你也许会说,找到一个 target,然后向左或向右线性搜索不行吗?可以,但是不好,因为这样难以保证二分查找对数级的复杂度了。怎么说呢,比如一个数组
[1,2,3,4,5,5,5,5,5,1],让你找出target==5的右侧边界,你一次二分可以把5找出来,但是,你需要比较五次线性搜索才能才能找到右侧边界,是不是就很难保证时间复杂度

2.2LeetCode 69.x的平方根

题目描述:
实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例 1:
输入: 4
输出: 2
示例 2:
输入: 8
输出: 2
说明: 8 的平方根是 2.82842…,
由于返回类型是整数,小数部分将被舍去。

双百二分:

class Solution {
   
public:
    int mySqrt(int x) {
   
     if(x==1||x==0)
     return x;
     int left=0;
     int right=x;
     int mid;
     int ans;
     while(left<=right)
     {
   
         mid=left+(right-left)/2;
         if((long long)mid*mid<=x)
         {
   
             ans=mid;
             left=mid+1;
         }
         else
         right=mid-1;
     }
     return ans;
    }
};
2.3LeetCode 374.猜数字大小

我们正在玩一个猜数字游戏。 游戏规则如下:
我从 1 到 n 选择一个数字。 你需要猜我选择了哪个数字。
每次你猜错了,我会告诉你这个数字是大了还是小了。
你调用一个预先定义好的接口 guess(int num),它会返回 3 个可能的结果(-1,1 或 0):
-1 : 我的数字比较小
1 : 我的数字比较大
0 : 恭喜!你猜对了!
示例 :
输入: n = 10, pick = 6
输出: 6

双百二分:

/** 
 * Forward declaration of guess API.
 * @param  num   your guess
 * @return 	     -1 if num is lower than the guess number
 *			      1 if num is higher than the guess number
 *               otherwise return 0
 * int guess(int num);
 */
class Solution {
   
public:
    int guessNumber(int n) {
   
        int left=0
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值