【优选算法】——二分查找!

目录

1、二分查找

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

3、搜索插入位置

4、x的平方根

5、山脉数组的封顶索引

6、寻找峰值

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

8、点名

9、完结散花


1、二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1

示例 1:

复制代码
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

复制代码
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -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) return mid;
            else if(nums[mid]<target) left=mid+1;
            else right=mid-1;
        }
        return -1;
    }
};

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

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

复制代码
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

示例 2:

复制代码
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

示例 3:

复制代码
输入:nums = [], target = 0
输出:[-1,-1]

解题代码:

复制代码
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.empty()) return {-1,-1};//处理边界情况
        vector<int> ret;
        int left=0,right=nums.size()-1;
        //求左端点
        while(left<right)//判断条件一定是<,如果<=则会进入死循环
        {
            int mid=left+(right-left)/2;//不能加1,否则死循环
            if(nums[mid]<target) left=mid+1;
            else right=mid;
        }
        ret.push_back(nums[right]==target?right:-1);
        //求右端点
        left=0,right=nums.size()-1;
        while(left<right)
        {
            int mid=left+(right-left+1)/2;//一定要加1,否则死循环
            if(nums[mid]>target) right=mid-1;
            else left=mid;
        }
        ret.push_back(nums[left]==target?left:-1);
        return ret;
    }
};

3、搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

复制代码
输入: nums = [1,3,5,6], target = 5
输出: 2

示例 2:

复制代码
输入: nums = [1,3,5,6], target = 2
输出: 1

示例 3:

复制代码
输入: nums = [1,3,5,6], target = 7
输出: 4

解题代码:

复制代码
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) right= mid;
            else left=mid+1;
        }
        return nums[left]<target?left+1:left;
    }
};

4、x的平方根

给你一个非负整数 x ,计算并返回 x算术平方根

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

注意: 不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5

示例 1:

复制代码
输入:x = 4
输出:2

示例 2:

复制代码
输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。

解题代码:

复制代码
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、山脉数组的封顶索引

给定一个长度为 n 的整数 山脉 数组 arr ,其中的值递增到一个 峰值元素 然后递减。

返回峰值元素的下标。

你必须设计并实现时间复杂度为 O(log(n)) 的解决方案。

示例 1:

复制代码
输入:arr = [0,1,0]
输出:1

示例 2:

复制代码
输入:arr = [0,2,1,0]
输出:1

示例 3:

复制代码
输入:arr = [0,10,5,2]
输出: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]) right=mid-1;
            else left=mid;
        }
        return left;
    }
};

6、寻找峰值

峰值元素是指其值严格大于左右相邻值的元素。

给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。

你可以假设 nums[-1] = nums[n] = -∞

你必须实现时间复杂度为 O(log n)的算法来解决此问题。

示例 1:

复制代码
输入:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。

示例 2:

复制代码
输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5 
解释:你的函数可以返回索引 1,其峰值元素为 2;
     或者返回索引 5, 其峰值元素为 6。
复制代码
class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int left=0,right=nums.size()-1;
        //求最右端点
        while(left<right)
        {
            int mid=left+(right-left+1)/2;
            if(nums[mid]<nums[mid-1]) right=mid-1;
            else left=mid;
        }
        return left;
    }
};

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

已知一个长度为 n 的数组,预先按照升序排列,经由 1n旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

复制代码
输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

示例 2:

复制代码
输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 3 次得到输入数组。

示例 3:

复制代码
输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。
复制代码
class Solution {
public:
    int findMin(vector<int>& nums) {
        int n=nums.size()-1;
        //处理特殊情况,即未发生旋转,或旋转到原数组
        if(nums[0]<nums[n]) return nums[0];
        int left=0,right=n;
        //求最左端点
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]<nums[0]) right=mid;
            else left=mid+1;
        }
        return nums[left];
    }
};

8、点名

某班级 n 位同学的学号为 0 ~ n-1。点名结果记录于升序数组 records。假定仅有一位同学缺席,请返回他的学号。

示例 1:

复制代码
输入: records = [0,1,2,3,5]
输出: 4

示例 2:

复制代码
输入: records = [0, 1, 2, 3, 4, 5, 6, 8]
输出: 7
复制代码
class Solution {
public:
    int takeAttendance(vector<int>& r) {
        int end=r.size()-1;
        //特殊情况处理
        if(r[end]==end) return end+1;
        int left=0,right=end;
        //求最左端点的二分查找
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(r[mid]>mid) right=mid;
            else left=mid+1;
        }
        return right;
    }
};

其他解法:

>hash映射

复制代码
class Solution {
public:
    int takeAttendance(vector<int>& r) {
        int n=r.size()+1;
        int hash[10000]={0};
        for(auto e:r) hash[e]++;
        for(int i=0;i<n;i++) 
        {
            if(hash[i]==0)
            return i;
        }
        return n;
    }
};

>位运算

复制代码
class Solution {
public:
    int takeAttendance(vector<int>& r) {
        int ret=0;
        for(int i=0;i<r.size();i++)
        {
            ret^=r[i];
            ret^=i;
        }
        return ret^=r.size();
    }
};

>暴力查找

复制代码
class Solution {
public:
    int takeAttendance(vector<int>& r) {
        int ret=0;
        for(int i=0;i<r.size();i++)
        {
            if(r[i]!=i) return i;
        }
        return r.size();
    }
};

>数学(高斯求和公式)

复制代码
class Solution {
public:
    int takeAttendance(vector<int>& r) {
        int ret=0,sum1=0,sum2=0;
        for(int i=0;i<r.size();i++)
        {
            sum1+=r[i];
            sum2+=i;
        }
        return sum2-sum1+r.size();
    }
};

9、完结散花

好了,这期的分享到 这里就结束了~

如果这篇博客对你有帮助的话,可以用你们的小手指点一个免费的赞并收藏起来哟~

如果期待博主下期内容的话,可以点点关注,避免找不到我了呢~

我们下期不见不散~~

​​​​

相关推荐
Haohao+++1 小时前
Stable Diffusion原理解析
人工智能·深度学习·算法
ideaout技术团队4 小时前
leetcode学习笔记2:多数元素(摩尔投票算法)
学习·算法·leetcode
代码充电宝4 小时前
LeetCode 算法题【简单】283. 移动零
java·算法·leetcode·职场和发展
不枯石6 小时前
Matlab通过GUI实现点云的均值滤波(附最简版)
开发语言·图像处理·算法·计算机视觉·matlab·均值算法
不枯石7 小时前
Matlab通过GUI实现点云的双边(Bilateral)滤波(附最简版)
开发语言·图像处理·算法·计算机视觉·matlab
白水先森8 小时前
C语言作用域与数组详解
java·数据结构·算法
想唱rap9 小时前
直接选择排序、堆排序、冒泡排序
c语言·数据结构·笔记·算法·新浪微博
老葱头蒸鸡10 小时前
(27)APS.NET Core8.0 堆栈原理通俗理解
算法
视睿10 小时前
【C++练习】06.输出100以内的所有素数
开发语言·c++·算法·机器人·无人机
柠檬071111 小时前
matlab cell 数据转换及记录
算法