【基础算法总结】二分查找二

二分查找二

点赞 👍👍收藏 🌟🌟关注 💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.山脉数组的峰顶索引

题目链接: 852. 山脉数组的峰顶索引

题目描述:

题目描述的有些复杂,换成图形就是一个山峰让你找最顶点

算法原理:

首先我们会想到遍历,找到这个数组中第一次出现当前这个值比下一个值大的下标,

解法一:暴力枚举 O(N)

题目要求O(logn),我们看看有没有优化的地方。我们发现这个峰顶值把数组分成了两部分,这时你会想到什么 二段性

解法二:二分查找算法

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;

    }
};

2.寻找峰值

题目链接: 162. 寻找峰值

题目描述:

这道题也是找峰值但是和上面不一样的是上面那道题数组就是上下一种情况,而这里数组可能会有三种情况:

算法原理:

解法一:暴力求解 O(N)

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

分析一下有没有优化的地方。因为我们就是比较当前位置和下一个位置的关系,所以我们把这个数组抽象出来。

  1. arr[i] > arr[i+1] ,此时是一个下降趋势,左边区间一定会存在一个峰值,左右都是负无穷,左边是从负无穷开始的,呈现上升趋势然后才下降,所以左边一定是有峰值的。但是右边区间不一定,因为右边是负无穷可能一直下降。接下来去左边寻找。
  2. arr[i] < arr[i+1],此时是一个上升趋势,左边是从服务器开始可能一直在上升,那可能是没有最终结果的。但是右边一定是有最终结果的,因为右边是负无穷,会降到负无穷的。接下来去右边寻找。

我们发现这两个点的关系把整个数组分成了两部分。然后我们去一个部分去搜索结果。

解法二:二分查找

这道题是严格无序的,因为可能存在一升一降、一升一降。但是我们找到了二段性,因此也可以用二分查找算法。

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;

    }
};

3.寻找旋转排序数组中的最小值

题目链接: 153. 寻找旋转排序数组中的最小值

题目描述:

注意题目给的是 互不相同 的元素

算法原理:

这道题暴力求解很容易想到办法,从前往后遍历然后找最小值就可以了

解法一:暴力求解 O(N)

但是我们观察之后,发现这个数组可以分成两段。A-B 递增 C-D也是递增。

假设我们已D为分割点,我们发现A-B内的值大于D,C-D内的值小于等于D。由此我们发现了二段性

解法二:二分查找算法

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

上面我们以D为分割点,那以A点为分割点可不可以?其实也是可以的,但是要注意可以旋转之后是一个严格递增的数组,这里要特别处理一下。以D为分割点不会碰到这个问题。可以自己分析一波

cpp 复制代码
class Solution {
public:
    int findMin(vector<int>& nums) {
        int left=0,right=nums.size()-1;
        int n=right;
        while(left<right)
        {
		    int mid=left+(right-left)/2;
		    if(nums[mid]>=nums[0]) left=mid+1;
		    else right=mid;
		}
         //旋转后严格单调递增
         if(nums[left]>nums[0]) return nums[0];
         return nums[left];
    }
};

4.点名

题目链接: LCR 173. 点名

题目描述:


算法原理:

这道题很简单,总要考察的是对于一种题你有几个解题思路

解法一:哈希表 时间O(N) 空间O(N)

把数组中所有数映射进hash表

cpp 复制代码
class Solution {
public:
    int takeAttendance(vector<int>& records) {
    
    	//1.哈希表
        int n=records.size()+1;
        int hash[10001]={0};
        for(int i=0;i<records.size();++i)
        {
            hash[records[i]]++;
        }
        
        for(int i=0;i<n;++i)
        {
            if(hash[i]==0) return i;
        }
    }
};

解法二:暴力遍历 O(N)

cpp 复制代码
class Solution {
public:
    int takeAttendance(vector<int>& records) {
    
    	//2.暴力遍历
        int n=0;
        for(int i=0;i<records.size();i++)
        {
            if(records[i] != n) break;
            ++n;
        }
        return n;
    }
};

解法三:位运算

将缺少的上面和不缺少的下面进行异或运算。异或运算相同为0,相异为1。最终会有正确结果。具体操作是将数字和下标异或,最后在异或一次就可以了

cpp 复制代码
class Solution {
public:
    int takeAttendance(vector<int>& records) {
    
        //3.位运算
        int x = 0;
        for (int i = 0; i < records.size(); i++)
            x ^= records[i] ^ i ;
        return x^records.size();
    }
};

解法四:等差数列求和

cpp 复制代码
class Solution {
public:
    int takeAttendance(vector<int>& records) {
    
    	//4.求和公式
        int n = records.size();
        int sum = (1+n)*n/2;
        for(int i = 0;i<records.size();++i)
        {
            sum-=records[i];
        }
        return sum;
    }
};

上面时间复杂度都是O(N),思考一下看看有没有优化的可能

由此发现二段性

解法五:二分查找算法

cpp 复制代码
class Solution {
public:
    int takeAttendance(vector<int>& records) {

        //0.二分查找
        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(left == records[left]) return left+1;
        else return left;
    }
};
相关推荐
minstbe18 分钟前
AI开发 - 算法基础 递归 的概念和入门(二)汉诺塔问题 递归的应用和使用注意 - Python
开发语言·python·算法
TANGLONG22243 分钟前
【初阶数据结构与算法】八大排序之非递归系列( 快排(使用栈或队列实现)、归并排序)
java·c语言·数据结构·c++·算法·蓝桥杯·排序算法
不想当程序猿_1 小时前
【蓝桥杯每日一题】与或异或——DFS
c++·算法·蓝桥杯·深度优先
就爱学编程1 小时前
力扣刷题:单链表OJ篇(下)
算法·leetcode·职场和发展
小白—人工智能1 小时前
有一个4*5的矩阵如下,要求编写程序计算总和与平均值,并找出其中值最大的那个元素输出,以及其所在的行号和列号。
数据结构·python·算法·矩阵
邂逅岁月1 小时前
滑不动窗口的秘密—— “滑动窗口“算法 (Java版)
算法·面试·求职招聘·创业创新·刷题
sunny-ll2 小时前
【C++】explicit关键字详解(explicit关键字是什么? 为什么需要explicit关键字? 如何使用explicit 关键字)
c语言·开发语言·c++·算法·面试
轩源源2 小时前
C++草原三剑客之一:继承
开发语言·数据结构·c++·算法·青少年编程·继承·组合
半盏茶香4 小时前
C语言勘破之路-最终篇 —— 预处理(下)
c语言·开发语言·c++·算法
pianmian18 小时前
贪心算法.
算法·贪心算法