当前位置: 首页 > article >正文

算法专题三: 二分查找

目录

  • 1. 朴素版: 二分查找
  • 2. 查找排序数组元素第一个和最后一个位置
  • 3. 搜索插入位置
  • 4. x的平方根
  • 5. 山脉数组的峰顶索引
  • 6. 寻找旋转数组中的最小值
  • 7. 点名

博客主页: 酷酷学!!!
感谢您的关注~


正文开始

1. 朴素版: 二分查找

在这里插入图片描述

题目思路: 仅需根据题意, 找出二段性, 正确更新下标位置即可.
在这里插入图片描述
在这里插入图片描述

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

2. 查找排序数组元素第一个和最后一个位置

在这里插入图片描述

算法原理:

那么本道题朴素的二分查找就不适用了, 我们需要根据二段行将数组进行划分, 得到下图结论.

在这里插入图片描述

这里的注意事项细节处理, 循环条件以及我们的求中点操作, 那么为什么这种算法就是对呢?

在这里插入图片描述

下面我们可以通过三种情况来细分, 如果有结果则相遇位置就是结果, 如果全大于t, 则会一直right向左移动最后相遇位置结束, 退出循环, 全小于t, 则left会一直向右移动, 最后退出循环, 那么为什么求左端点第二种求中点操作死循环呢?

在这里插入图片描述
int mid = left + (right - left + 1)/2, 对于这种写法, 假设剩下最后两个元素, 这两区别无非就是一个拿到前一个元素, 一个拿到后一个元素, 如果我们要求左端点拿到后面的元素, 如果muns[mid] < target. left会变成mid+1, 出循环, 如果nums[mid] <= target,则right会一直等于mid的位置, 陷入死循环.

在这里插入图片描述

故, 求右端点类似

在这里插入图片描述

总结一下: 如何让二分从恶心变成easy~

在这里插入图片描述

编写代码:

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.empty()) return {-1,-1};//处理为空的情况
        int left = 0 ,right = nums.size() - 1;
        int begin = 0, end = 0;
        while(left < right)
        {
            int mid = left + (right - left)/2;
            if(nums[mid] < target)
                left = mid + 1;
            else right = mid;
        }
        begin = left;
        if(nums[left] != target) return {-1,-1}; //处理未找到的情况
        right = nums.size() -1;
        while(left < right)
        {
            int mid = left + (right - left + 1)/2;
            if(nums[mid] <= target)
                left = mid;
            else right = mid - 1;
        }
        end = left;
        return {begin,end};
    }
};

3. 搜索插入位置

在这里插入图片描述

算法思路:

根据题目我们很容易发现二段性, 需要待插入的位置就为左端点, 注意, 如果所有数据都小于target则相遇位置为最后一个位置, 待插入位置为相遇位置的下一个位置.

在这里插入图片描述

编写代码:

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

4. x的平方根

在这里插入图片描述

算法思路:

我们根据题意不难找出二段性, 要查找的位置为右端点, 列出判断语句即可.

在这里插入图片描述

编写代码:

class Solution {
public:
    int mySqrt(int x) {
        long long left = 0, right = x;
        while(left < right)
        {
            long long mid = left + (right - left + 1) / 2; 
            if(mid * mid <= x)
                left = mid;
            else right = mid - 1;
        }
        return left;
    }
};

5. 山脉数组的峰顶索引

在这里插入图片描述

算法思路:

根据二段性, 分出左右数组, 注意如果判断语句中有-1, 则计算mid有+1.

在这里插入图片描述

编写代码:

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

6. 寻找旋转数组中的最小值

在这里插入图片描述

算法思路:

以最后一个数为基准值进行比较, 即可将数组划分成两部分, 这里不可以以nums[0]进行划分, 因为当数组有序时, 相遇位置会在最后一个位置导致结果错误, 需要特殊处理.

在这里插入图片描述

编写代码:

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

以nums[0]为基准值划分

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

7. 点名

在这里插入图片描述

算法思路:

本道题解法多种, 但是二分查找时间复杂度最低, 根据题目不难发现根据下标即可划分出数组, 但是注意判断, 当数组有序时, 缺失位置为最后一个位置的下一个位置, 这里指针相遇的位置需要最后+1.

在这里插入图片描述

编写代码:

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

完, 感谢点赞收藏!!!


http://www.kler.cn/news/339002.html

相关文章:

  • 【STM32单片机_(HAL库)】4-2-1【定时器TIM】定时器输出PWM实现呼吸灯实验
  • vue3组件通信之defineEmits
  • (C语言贪吃蛇)15.贪吃蛇吃食物
  • QD1-P5 HTML常用标签:段落和换行
  • 麒麟系统串口配置篇
  • Qt教程(001):Qt概述与安装
  • 如何实现 C/C++ 与 Python 的通信?
  • 【算法笔记】二分算法原理的深度剖析
  • 数据分析认知
  • 17 链表——21. 合并两个有序链表 ★
  • 【小技巧】mysql 判断表字段是否存在 删除字段 sql脚本
  • python爬虫 - 进阶requests模块
  • 【机器学习-无监督学习】降维与主成分分析
  • 命名管道Linux
  • Stable Diffusion绘画 | 如何做到不同动作表情,人物角色保持一致性(下篇)
  • 深度学习中的迁移学习:预训练模型微调与实践
  • 2024年liunx安装openvino非源码编译版(比源码编译简单!)
  • APP自动化搭建与应用
  • Linux 学习
  • 推荐!专业Substance 3D Painter v10.解锁版下载及安装 (3D绘画软件)