优选算法专题3:二分查找

二分查找

目录

二分查找

试题一:二分查找

算法原理

代码编写

试题2:在排序数组中查找元素第一个和最后一个位置

算法原理

代码编写

试题3:搜索插入位置

算法原理

代码编写

试题4:x的平方根

算法原理

代码编写

试题5:山峰数组的峰顶

算法原理

代码编写

试题6:寻找峰值

算法原理

代码编写

试题7:寻找旋转排序数组中的最小值

算法原理

代码编写

试题8:点名

算法原理

代码编写


试题一:二分查找

算法原理

解法一:暴力枚举

从左往右遍历一遍数组

时间复杂度:O(N)

解法二:二分查找算法

利用二段性,实现二分查找

循环结束的条件为left > right

二分查找算法的效率:O(logN)

朴素二分模板

代码编写

  • 个人版本
cpp 复制代码
class Solution 
{
public:
    int search(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 if(nums[mid] > target)
            {
                right = mid - 1;
            }
            else
            {
                return mid;
            }
        }
        return -1;
    }
};
  • 标准版本
cpp 复制代码
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:在排序数组中查找元素第一个和最后一个位置

算法原理

解法一:暴力查找

从左往右遍历

时间复杂度:O(N)

解法二:二分算法

朴素二分只能找到这个数值,但是并不知道起始位置与终止位置

所以需要利用二段性进行优化

查找区间的左端点

查找区间的右端点

代码编写

  • 个人版本
cpp 复制代码
class Solution
{
public:
    vector<int> searchRange(vector<int>& nums, int target)
    {
        vector<int> ret;
        if (nums.size() == 0)
        {
            ret = {-1, -1};
            return ret;
        }
        //找左边界
        int first_left = 0;
        int first_right = nums.size() - 1;
        while (first_left < first_right)
        {
            int first_mid = first_left + (first_right - first_left) / 2;
            if (nums[first_mid] < target)
            {
                first_left = first_mid + 1;
            }
            else
            {
                first_right = first_mid;
            }
        }
        //判断target是否不存在
        if (nums[first_left] != target)
        {
            ret = {-1, -1};
            return ret;
        }

        //找右边界
        int last_left = 0;
        int last_right = nums.size() - 1;
        while (last_left < last_right)
        {
            int last_mid = last_left + (last_right - last_left + 1) / 2;
            if (nums[last_mid] > target)
            {
                last_right = last_mid - 1;
            }
            else
            {
                last_left = last_mid;
            }
        }
        ret = {first_left, last_left};
        return ret;
    }
};
  • 标准版本
cpp 复制代码
class Solution 
{
public:
    vector<int> searchRange(vector<int>& nums, int target) 
    {
       //处理边界情况
       if(nums.size() == 0) return {-1,-1};
       int begin = 0;
       //#1:二分左端点
       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{-1,-1};
       else begin = left;//标记左端点

       //#2:二分右端点
       left = 0,right = nums.size() - 1;
       while(left < right)
       {
            int mid = left + (right - left + 1) / 2;
            if(nums[mid] <= target) left = mid;
            else right = mid - 1;
       }
       return {begin,right};
    }
};

试题3:搜索插入位置

算法原理

查找区间左端点

代码编写

  • 个人版本
cpp 复制代码
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;
            }
        }
        if(target > nums[nums.size() - 1])
        {
            return nums.size();
        }
        return left;
    }
};
  • 标准版本
cpp 复制代码
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的平方根

算法原理

查找区间右端点

代码编写

  • 个人版本
cpp 复制代码
class Solution 
{
public:
    int mySqrt(int x) 
    {
        if(x == 0)
        {
            return 0;
        }
        long long right = x;
        long long left = 0;
        while(left < right)
        {
            long long mid = left + (right - left + 1) / 2;
            if(mid * mid > x)
            {
                right = mid - 1;
            }
            else
            {
                left = mid;
            }
        }
        return right;
    }
};
  • 标准版本
cpp 复制代码
class Solution 
{
public:
    int mySqrt(int x) 
    {
        if(x < 1) return 0;//处理边界情况
        long long left = 1,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:山峰数组的峰顶

算法原理

解法一:暴力枚举

时间复杂度:O(N)

解法二:二分查找

利用二段性,查找区间右端点

代码编写

  • 个人版本
cpp 复制代码
class Solution 
{
public:
    int peakIndexInMountainArray(vector<int>& arr) 
    {
        int left = 0;
        int 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;
    }
};
  • 标准版本
cpp 复制代码
class Solution 
{
public:
    int peakIndexInMountainArray(vector<int>& arr) 
    {
        int left = 1,right = arr.size() - 2;
        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:寻找峰值

算法原理

解法一:暴力解法

从第一个位置一直往后走,分情况讨论

时间复杂度:O(N)

解法二:二分查找

在严格无序的情况下,如果发现二段性,依旧能够使用二分算法

代码编写

  • 个人版本
cpp 复制代码
class Solution 
{
public:
    int findPeakElement(vector<int>& nums) 
    {
        int left = 0;
        int right = nums.size() - 1;
        while(left < right)
        {
            int mid = left + (right - left) / 2;
            if(nums[mid] > nums[mid + 1])
            {
                right = mid;
            }
            else
            {
                left = mid + 1;
            }
        }
        return left;
    }
};
  • 标准版本
cpp 复制代码
class Solution 
{
public:
    int findPeakElement(vector<int>& nums) 
    {
        int left = 0,right = nums.size() - 1;
        while(left < right)
        {
            int mid = left + (right - left) / 2;
            if(nums[mid] > nums[mid + 1]) right = mid;
            else left = mid + 1;
        }
        return left;
    }
};

试题7:寻找旋转排序数组中的最小值

算法原理

解法一:暴力查找最小值

时间复杂度:O(N)

解法二:二分查找算法

代码编写

  • 个人版本
cpp 复制代码
class Solution 
{
public:
    int findMin(vector<int>& nums) 
    {
        int left = 0;
        int right = nums.size() - 1;
        while(left < right)
        {
            int mid = left + (right - left) / 2;
            if(nums[mid] > nums[nums.size() - 1])
            {
                left = mid + 1;
            }
            else
            {
                right = mid;
            }
        }
        return nums[left];
    }
};
  • 标准版本
cpp 复制代码
class Solution 
{
public:
    int findMin(vector<int>& nums) 
    {
        int left = 0,right = nums.size() - 1;
        int x = nums[right];
        while(left < right)
        {
            int mid = left + (right - left) / 2;
            if(nums[mid] > x) left = mid + 1;
            else right = mid;
        }
        return nums[left];
    }
};

试题8:点名

算法原理

O(N)解法:

哈希表

直接遍历

位运算

高斯求和

O(logN)解法:二分查找

代码编写

  • 个人版本
cpp 复制代码
class Solution 
{
public:
    int takeAttendance(vector<int>& records) 
    {
        int left = 0;
        int right = records.size() - 1;
        while(left < right)
        {
            int mid = left + (right - left) / 2;
            if(records[mid] == mid)
            {
                left = mid + 1;
            }
            else
            {
                right = mid;
            }
        }
        return records[left] == left ? records.size() : left;
    }
};
  • 标准版本
cpp 复制代码
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;
       }
       return records[left] == left ? left + 1 : left;
    }
};
相关推荐
Byte不洛2 小时前
理解C++异常机制:栈展开、异常传播与异常安全
c++·异常处理·后端开发·编程基础·try catch
代码飞一会儿2 小时前
算法与数据结构之树——让数据查找更加迅速
算法
工业甲酰苯胺2 小时前
2026 产业 AI 爆发:JNPF 助企业少走 3 年弯路
人工智能·算法·低代码
贾斯汀玛尔斯2 小时前
每天学一个算法--最短路径问题与三类基本算法
算法
我头发多我先学2 小时前
C++ AVL 树:平衡原理到完整实现(自平衡二叉搜索树)
开发语言·数据结构·c++·算法
啊我不会诶2 小时前
2025浙江省赛补题
c++·算法
@小柯555m2 小时前
算法(字母异位词分组)
java·开发语言·算法·leetcode
故事和你912 小时前
洛谷-算法2-1-前缀和、差分与离散化2
开发语言·数据结构·算法·深度优先·动态规划·图论
贾斯汀玛尔斯2 小时前
每天学一个算法--DFS / BFS
算法·深度优先·宽度优先