优选算法专题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;
    }
};
相关推荐
洛水水5 小时前
【力扣100题】33.验证二叉搜索树
算法·leetcode·职场和发展
SimpleLearingAI5 小时前
聚类算法详解
算法·数据挖掘·聚类
刀法如飞6 小时前
Go 字符串查找的 20 种实现方式,用不同思路解决问题
算法·面试·程序员
Byron Loong7 小时前
【c++】为什么有了dll和.h,还需要包含lib
java·开发语言·c++
Dlrb12117 小时前
C语言-指针数组与数组指针
c语言·数据结构·算法·指针·数组指针·指针数组·二级指针
WL_Aurora7 小时前
Python 算法基础篇之集合
python·算法
坚果派·白晓明8 小时前
【鸿蒙PC三方库移植适配框架解读系列】第一篇:Lycium C/C++ 三方库适配 — 概述与环境配置
c语言·开发语言·c++·harmonyos·开源鸿蒙·三方库·c/c++三方库
平行侠8 小时前
A15 工业路由器IP前缀高速检索与内存压缩系统
网络·tcp/ip·算法
咩咦9 小时前
C++学习笔记02:cin 和 cout 输入输出
c++·学习笔记·cin·输入输出·cout
咩咦9 小时前
C++学习笔记05:引用和常引用
c++·学习笔记·引用·const·常引用