二分查找算法

1. LeetCode第704题—二分查找

链接:https://leetcode-cn.com/problems/binary-search/
在这里插入图片描述

class Solution {
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;
            else if(nums[mid] < target)
                left = mid +1;
            else
                right = mid -1;  
        }
        return -1;
    }
};

2. LeetCode第35题—搜索插入的位置

链接: https://leetcode-cn.com/problems/search-insert-position/
在这里插入图片描述
题解:对于二分法来说,如果想要找的target就在数组中,那么返回的下标mid就是我们要的结果,但是如果他不在数组中,此时想要这个target的插入位置的时候就应该是left的位置

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        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;  
        }
        //如果全部搜索完了都没有找到我们想要的target,此时就应该把他插入进去
        //这一步才是这道题的精髓地方
        //究竟应该插在什么位置是应该好好考虑的
        return left;//这个位置才是应该插入的位置
    }
};

3. 遇见排序数组查找数字,第一反应就是二分

因为遍历整个数组查找的时间复杂度为O(N),但是如果是二分查找算法的话,他的时间复杂度就可以由线性的O(N)降低为对数级别O(logN)。

3.1 剑指offer11题—旋转数组的最小数字

链接: https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/
在这里插入图片描述

题解:这道题并不简单,官方讲解视频非常的好,听完收获满满。

  • 暴力法
    我从头向后去遍历,如果找到了前一个元素比后一个元素大,那么此时分界点就在这两个元素之间,然后就可以找到最小的元素。但是这里还忽略了一种情况,那就是如果是有重复元素的数组(且整个数组元素都相同),那么此时暴力法就解不出来了。在这里插入图片描述
  • 二分法(强烈推荐)
    因为可以把这个旋转数组看成两个递增的数组。
  1. 何时mid需要++,或者- -,在标准的二分查找中,left = mid+1,right = mid-1,但是这里为什么left = mid +1 ,right = mid,怎么理解?其实mid是否需要加加还是减减真正的考虑在于,是否需要考虑到mid位置本身这个点元素是否是我们需要的。在这道题中,如果mid > 最右边的值,那么此时最小值肯定在mid的右侧,它本身是不可能是的,所以这个点要跨过去,但是如果mid < 最右边的值,说明此时mid的右侧都是升序数组,但是并不敢保证是否mid本身这个点的元素就是我们要的最小的值,所以这个点不能够跨过。

  2. 这题还有一个就是right- -,这一步是在干什么? 是在去重,并且这一步并不会对结果产生影响。(也就是暴力的缩短数组,那么此时mid所待的位置,也就是3,此时就可以确定分界线是在mid的左边)
    在这里插入图片描述

  3. 这题的时间复杂度是多少? 在考虑到最坏的情况下,如果里面不含有重复的元素,那么时间复杂度就是O(logN),但是如果考虑到重复的元素,那么万一整个数组都是相同的元素,那么他的遍历过程就相当于从后往前缩短了整个数组。
    在这里插入图片描述

class Solution {
public:
    int minArray(vector<int>& numbers) {
        //如果找到的中位数的位置是比起始的位置大,那么此时它还位于左边的升序数组中
        //如果找到的中位数比最右边的数都要小,那么此时他位于右边的升序数组中
        int left = 0,right = numbers.size()-1;
        while(left<right)
        {
            int mid = left + (right-left)/2;
            if(numbers[mid] > numbers[right])
            {
                //如果中间的数大于最右边的值,那么最小值一定在mid的右侧
                left = mid + 1;
            }
     //此时如果中间的值小于最右边的值,那么此时有两种可能,一种是mid本身就是,还有一种是在mid的左侧
            else if(numbers[mid] < numbers[right])
            {
                right = mid;
            }
            else 
            {
                //这一步是为了去重,因为有可能数组中会有重复的元素
                right -= 1;
            }
        }
        return numbers[left];
    }
};

3.2 剑指offer153题—寻找旋转排序数组中的最小值

在这里插入图片描述
解题思路:到底这个mid是+1还是-1,最终还是取决于分析是否需要这个点,如果不需要那么就可以选择直接跳过去。

class Solution {
public:
    int findMin(vector<int>& nums) {
        //题目所给出的是一个所有元素都互不相同的数组
        int left = 0,right = nums.size()-1;
        if(nums[left] < nums[right])
            return nums[0];
        while(left < right)
        {
            int mid = left + (right - left) / 2;
            if(nums[mid] < nums[right])
                right = mid;
            else
                left = mid + 1;
        }
        //最终left和right应该是停留在同一个地方的
        return nums[left];
    }
};

3.3 剑指offer53题—在排序数组中查找数字I

链接:https://leetcode-cn.com/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/
在这里插入图片描述

class Solution {
public:
    int binarySearch(vector<int>& nums, int target)
    {
        int left = 0,right = nums.size()-1;
        while(left <= right)
        {
            int mid = left + (right - left) / 2;
            if(nums[mid] > target)
            {
                right = mid-1;
            }
            else if(nums[mid] < target)
            {
                left = mid+1;
            }
            else
            {
                return mid;
            }
        }
        return -1;
    }
    int search(vector<int>& nums, int target) {
        if(nums.size() == 0)
            return 0;
        int keyindex = binarySearch(nums,target); //这就得到了我们想找的target的下标
        if(keyindex == -1)
            return 0;
        //此时开始左右搜索
        int m = keyindex-1;
        int n = keyindex+1;
        int count = 1; //本身mid的位置就是一个
        while(m >=0 &&nums[m--] ==  target)
            count++;
        while(n<nums.size() && nums[n++] == target)
            count++;
        return count;
    }
};

3.4 剑指offer53题—0~n-1中缺失的数字

链接: [link]https://leetcode-cn.com/problems/que-shi-de-shu-zi-lcof/
在这里插入图片描述

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        //对于从0开辟的数组来说,nums[i] = i,
        //但是如果此时缺少一个的话,那么那个缺少的位置下标应该是nums[i] != i
        //通过二分法找那个位置下标不对的位置
        int left = 0,right = nums.size()-1;
        while(left<=right)
        {
            int mid = left + (right-left)/2;
            if(nums[mid] == mid)
            {
                //说明左边是合法的,都是满足条件的
                left = mid + 1;
            }
            else
            {
                //说明到mid这个点的时候已经开始不相等了,那么前面就开始不相等了
                right = mid -1;
            }
        }
        return left;
    }
};

4. LeetCode第278题—第一个错误版本

链接: https://leetcode-cn.com/problems/first-bad-version/submissions/
在这里插入图片描述

// The API isBadVersion is defined for you.
// bool isBadVersion(int version);

class Solution {
public:
    int firstBadVersion(int n) {
        int left = 1,right = n;
        while(left <= right)
        {
            int mid = left + (right-left)/2;
            if(isBadVersion(mid))
            {
                //==true,说明这个地方已经坏了
                right = mid-1;
            }
            else
            {
                left = mid + 1;
            }
        }
        return left;
    }
};

5. LeetCode第34题—在排序数组中查找元素的第一个和最后一个位置

链接: https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/
在这里插入图片描述

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        //首先进行二分查找,然后在左右搜索
        vector<int> v(2,-1);
        if(nums.size() == 0)
            return v;
        int midindex = Binarysearch(nums,target);
        if(midindex == -1)
            return v;
        int leftindex = midindex;
        int rightindex = midindex;
        while(leftindex >=0 && nums[leftindex] == target)
            leftindex--;
        while(rightindex < nums.size() && nums[rightindex] == target)
            rightindex++;
        return {leftindex+1,rightindex-1}; //数组的返回可以使用花括号
    }

    int Binarysearch(vector<int>& nums, int target) {
        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;
    }
};

6.1 LeetCode第50题— Pow(x, n)

在这里插入图片描述
解题思路:快速幂法,本质上还是使用了二分法的思想,比如说我想要求2的8次方,那么如果我知道2的4次方的结果,我在平方就可以得到我要的结果,那么我要求2的4次方,只需要知道2的2次方,再平方就可以,依次递归下去,既然是递归肯定要有递归停止的条件,那么当n== 0, n== 1,n==-1,这些都算做停止的条件

class Solution {
public:
    double myPow(double x, int n) {
        //使用快速幂的方法,本质就是二分的思想
        //我需要知道x的n次方,那么如果我知道x的n/2次方然后再平方那么我就可以知道答案,这样一次类推下去,
        //下面这里是递推停止的条件
        if(n == 0)  
            return 1;
        if(n == 1)
            return x;
        if(n == -1)
            return 1 / x;
        double half = myPow(x,n>>1);
        //这里的按位与的意思就是来查看这个n的奇偶性的
        return n&1 ? half * half * x:half * half;
    }
};

6.2 LeetCode第69题—x的平方根

在这里插入图片描述

class Solution {
public:
    //典型的一道二分法的思想
    int mySqrt(int x) {
        if (x == 0) {
            return 0;
        }
        if (x == 1) {
            return 1;
        }
        //除了0以外的任何数的平方根都是大于0的
        //且任何一个数的算数平方根都是小于他的一般的
        int left = 1;
        int right = x;
        // 在区间 [left..right] 查找目标元素
        while (left < right) {
            int mid = left + (right - left + 1) / 2;
            // 注意:这里为了避免乘法溢出,改用除法
            if (mid > x / mid) {
                // 下一轮搜索区间是 [left..mid - 1]
                right = mid - 1;
            } else { 
                // 下一轮搜索区间是 [mid..right]
                left = mid;
            }
        }
        return left;
    }
};

7. LeetCode第33题— 搜索旋转排序数组

在这里插入图片描述
题解思路:既然看到了排序,呢么就要想到二分法,因为这样可以降低时间复杂度,对于二分法的时间复杂度是O(logN),虽然说他不是传统意义上的哪一种有序,但是他是一个范围区间上的有序,所以我们依旧可以使用二分法的思想来解决这道题,降低这道题的时间复杂度的情况。

class Solution {
public:
    //这道题肯定要使用二分法才是最恰当的
    int search(vector<int>& nums, int target) {
        //但是这里面说了这道题中的数值是各不相同的
        if(nums.size() == 0)
            return -1;
        if(nums.size() == 1 && target == nums[0])
            return 0;
        int left = 0,right = nums.size()-1;
        while(left <= right)
        {
            int mid = left + (right-left) / 2;
            if(nums[mid] == target)
                return mid;
            //对于所给的示例,我们可以进行假设,这里就是0和6来带入看一下代码是否可以解释的通
            if(target < nums[mid])
            {
                //这一段代码表示,他要比mid左边的所有数都小,且左边是有序的
                //如果不加这个=,那么在测试用例[3,1] 1 的时候就会出现错误
                if(nums[left] <= nums[mid] && target < nums[left])
                    left = mid + 1;
                else
                    right = mid -1;
            }
            else
            {
                //对于这一段代码我们可以带入的示例就是,上面示例依旧进行了一次循环,现在剩下0,1,2这三个数的时候,进行的循环,所需要考虑的,假设此时需要的0或者是2
                //这一段代码表示mid右边的是有序的
                if(nums[right] >= nums[mid] && target > nums[right])
                    right = mid -1;
                else
                    left = mid + 1;
            }
        }
        return -1;
    }
};

8. LeetCode第74题— 搜索二维数组

在这里插入图片描述
解题思路:其实使用上题目中所给出的所有条件就可以发现,如果把这个二维数组给转换为一维,那么这个数组是一个升序数组,对于升序数组我们就可以使用二分查找的方式来降低时间复杂度。

class Solution {
public:
    //你直接遍历这个二维数组多少有点傻逼了,因为你并没有使用到题目中明确所规定的几个点
    //使用这种方式就太过于简单了,不能这样做呀。
    //但是你仔细感觉就会发现这道题其实可以把二维变成一维,你把每一行都拼接起来
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size();
        int n = matrix[0].size();
        int left = 0,right = m*n-1;
        while(left <= right)
        {
            int mid = left + (right-left) / 2;
            //上面的那个mid算出来的是下标,然后我们要找到真正的这个数,在二维数组中
            int x = matrix[mid/n][mid%n];
            if(x < target)
                left = mid+1;
            else if(x > target)
                right = mid-1;
            else
                return true;
        }
        return false;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值