• 算法综合篇专题三:二分法


    bf1f8397c9d44264b5443464559ec884.jpeg

    "寻一颗,未萌的渺小啊,随着青翠未来,升入辽阔云霄~" 


            现在你有一个"升序"数组,想让你在这个数组里完成查找数字n,在这个数组内的下标,你可以怎么做?这也许是不少友子们初遇二分问题的场景。你可以使用O(N)的时间复杂度,对该数组进行遍历,就像这样。

    1. void FindNum(vector<int>& arr,int n)
    2. {
    3. for(int i=0;isize();++i)
    4. {
    5. if(arr[i] == n) return i;
    6. }
    7. return -1;
    8. }

            可是我们没有很好地利用到数组“有序”的特点,我们可以令数字为mid,那么借着有序的特点,可以将这个数组划分为两个区域,一边是小于mid的数,一边是大于mid的数。

    1. void FindNum(vector<int>& arr,int n)
    2. {
    3. int left = 0,right = arr.size()-1;
    4. while(left < right)
    5. {
    6. int mid = (left + right) / 2;
    7. if(arr[mid] < n) mid = left+1;
    8. else if(arr[mid] > m ) mid = right-1;
    9. else mid;
    10. }
    11. return -1;
    12. }

            所以,按照这样的算法查找数组中的某个数,时间复杂度可以下降为O(logN),是一个特别大的提升,但使用这个算法的前前提的 “数组有序”。

    ——前言

    1、二分查找

    (1) 题目解析        f9a3f845bb3b42d1bb3e95c62a56779c.png

            这道题是最朴素的二分查找,同前言举的例子是一样的解题思路。

     

    (2) 算法原理        

    d0056ce375774868b00a1223b6035f8e.png

    1. class Solution {
    2. public:
    3. int search(vector<int>& nums, int target) {
    4. int left = 0,right = nums.size()-1;
    5. // 当left==right时 当前元素是没有判断的
    6. // 因此这里需要再循环一次
    7. while(left <= right)
    8. {
    9. int mid = (left + right) / 2;
    10. if(nums[mid] > target){
    11. right = mid - 1;
    12. }
    13. else if(nums[mid] < target){
    14. left = mid + 1;
    15. }
    16. else return mid;
    17. }
    18. return -1;
    19. }
    20. };

     


     

    2、在排序数组中查找元素的第⼀个和最后⼀个位置

    (1) 题目解析

    31e02142d2b44dfeaf3533c880c94c9f.png

            根据数组"非递减顺序" 使用二分查找但朴素的二分查找只适用于查找一个数,所以这道题需要变形。


    (2) 算法原理        bee3964a73014e459e951d3ce5b9467d.png

            查找区间右端点也是类似过程,只是需要注意细节处理:

    e9b4f7b024fa484c93c2f16673eb3ef3.png

    1. class Solution {
    2. public:
    3. vector<int> searchRange(vector<int>& nums, int target) {
    4. if(nums.empty()) return {-1,-1};
    5. int left = 0,right = nums.size()-1;
    6. vector<int> ret;
    7. // 找左端点
    8. while(left < right)
    9. {
    10. int mid = left + (right - left) / 2;
    11. if(nums[mid] < target){
    12. left = mid + 1;
    13. }
    14. else right = mid;
    15. }
    16. // 此时找到了左端点
    17. if(nums[left] != target) ret.push_back(-1);
    18. else ret.push_back(left);
    19. right = nums.size()-1;
    20. // 找右端点
    21. while(left < right)
    22. {
    23. int mid = left + (right - left + 1) /2;
    24. if(nums[mid] > target){
    25. right = mid - 1;
    26. }
    27. else left = mid;
    28. }
    29. if(nums[right] != target) ret.push_back(-1);
    30. else ret.push_back(right);
    31. return ret;
    32. }
    33. };

     

    3、搜索插⼊位置

    (1) 题目解析

    098af4949fcd4dc6821c14f9fc9b4cf8.png

            这道题可以使用左端点和右端点解决。

    (2) 算法原理

    722822375de941c0b5911f36e3a9dd84.png

    左端点:

    1. class Solution {
    2. public:
    3. int searchInsert(vector<int>& nums, int target) {
    4. int left = 0,right = nums.size()-1;
    5. while(left < right)
    6. {
    7. int mid = left + (right - left) / 2;
    8. if(nums[mid] < target){
    9. left = mid + 1;
    10. }
    11. else right = mid;
    12. }
    13. // 可能该数不存在并且 > 当前数
    14. if(nums[left] < target) return left + 1;
    15. return left;
    16. }
    17. };

    右端点:

    1. class Solution {
    2. public:
    3. int searchInsert(vector<int>& nums, int target) {
    4. int left = 0,right = nums.size()-1;
    5. while(left < right)
    6. {
    7. int mid = left + (right - left + 1) / 2;
    8. if(nums[mid] > target){
    9. right = mid - 1;
    10. }
    11. else left = mid;
    12. }
    13. // 可能该数不存在并且 > 当前数
    14. if(nums[left] < target) return left + 1;
    15. return left;
    16. }
    17. };

           


     

    4. x 的平方根 

    (1) 题目解析         0581cd0a3b844e88adeb4ba2a391fea6.png

     

    (2) 算法原理        4e5a1bd44eae4391b2c0a77bbc8c048b.png

    1. class Solution {
    2. public:
    3. int mySqrt(int x) {
    4. if(x < 1) return 0;
    5. // 1~x
    6. int left = 1,right = x;
    7. while(left < right)
    8. {
    9. int mid = left + (right -left + 1) / 2;
    10. if(x < pow(mid,2))
    11. {
    12. right = mid - 1;
    13. }
    14. else left = mid;
    15. }
    16. return left;
    17. }
    18. };

     

    5、山脉数组的峰顶索引

    (1) 题目解析

    18e121e9c9254959ac3597d139e5efa2.png

            

    (2) 算法原理

    2f7134c2f8374765ad370b7344f64045.png

    左端点:

    1. class Solution {
    2. public:
    3. int peakIndexInMountainArray(vector<int>& arr) {
    4. int left = 1,right = arr.size()-2;
    5. // [left,mid] [mid+1,right]
    6. while(left < right)
    7. {
    8. int mid = left + (right - left) / 2;
    9. // 左端点
    10. if(arr[mid] < arr[mid+1]){
    11. left = mid + 1;
    12. }
    13. else right = mid;
    14. }
    15. return right;
    16. }
    17. };

     

    右端点: 

    1. class Solution {
    2. public:
    3. int peakIndexInMountainArray(vector<int>& arr) {
    4. int left = 1,right = arr.size()-2;
    5. // [left,mid] [mid+1,right]
    6. while(left < right)
    7. {
    8. int mid = left + (right - left + 1) / 2;
    9. if(arr[mid] < arr[mid-1]){
    10. right = mid - 1;
    11. }
    12. else left = mid;
    13. }
    14. return left;
    15. }
    16. };

    6、寻找峰值 

    (1) 题目解析

    8c385c411aa546dab34c7ce0f00c9921.png

     

    (2) 算法原理         15ba960526754aaeb048145681e8c555.png

    1. class Solution {
    2. public:
    3. int findPeakElement(vector<int>& nums) {
    4. if(nums.size() == 1) return 0;
    5. int left = 0,right = nums.size() - 1;
    6. while(left < right)
    7. {
    8. // 左端点发
    9. int mid = left + (right - left) / 2;
    10. if(nums[mid] < nums[mid+1]){
    11. left = mid+1;
    12. }
    13. else right = mid;
    14. }
    15. return left;
    16. }
    17. };

     


     

    7、寻找旋转排序数组中的最小值

    (1) 题目解析

    83f6ac6ebff14fb8befd700ce9b59604.png
            如何找到本题的二段性是比较难点。

     

    (2) 算法原理

            72dfd2a58eeb41398f0fe71bd26c26ae.png

    1. class Solution {
    2. public:
    3. int findMin(vector<int>& nums) {
    4. int left = 0,right = nums.size()-1;
    5. int x = nums[right]; //标记参照点
    6. while(left < right)
    7. {
    8. int mid = left + (right - left) / 2;
    9. if(nums[mid] > x){
    10. left = mid + 1;
    11. }
    12. else right = mid;
    13. }
    14. return nums[left];
    15. }
    16. };

     


    8、II. 0~n-1中缺失的数字

    (1) 题目解析

    c4ecbe361f0f4c85b4c343eb8dbfc8bf.png

     

    (2) 算法原理        

    1. class Solution {
    2. public:
    3. int missingNumber(vector<int>& nums) {
    4. int left = 0,right = nums.size()-1;
    5. while(left < right)
    6. {
    7. int mid = left + (right-left) / 2;
    8. if(nums[mid] == mid) {
    9. left = mid + 1;
    10. }
    11. else right = mid;
    12. }
    13. // left为0时 特殊处理
    14. return left == nums[left] ? left+1:left;
    15. }
    16. };

    本篇到此结束,感谢你的阅读。

    祝你好运,向阳而生~

    d6af3a90a3034d789408339d5d1ed37e.jpeg

     

  • 相关阅读:
    基于Android studio实习生招聘系统APP java
    【数据库查询表结构】
    chatgpt prompt提示词
    CSDN 创作规范
    上交所Binary行情接口demo
    SpringSecurity自定义权限不足页面
    软件测试大环境求职难,跳槽难?我在大军中异军突起
    算法通关村第三关-白银挑战双指针思想
    Less的基本语法
    vue:计算属性,监视属性,绑定class样式与style样式,自定义vue实例代码段
  • 原文地址:https://blog.csdn.net/RNGWGzZs/article/details/132815095