【专题三】二分查找(2)

📝前言说明:

  • 本专栏主要记录本人的基础算法学习以及LeetCode刷题记录,按专题划分
  • 每题主要记录:(1)本人解法 + 本人屎山代码;(2)优质解法 + 优质代码;(3)精益求精,更好的解法和独特的思想(如果有的话)
  • 文章中的理解仅为个人理解。如有错误,感谢纠错

🎬个人简介:努力学习ing
📋本专栏:C++刷题专栏
📋其他专栏:C语言入门基础python入门基础C++学习笔记Linux
🎀CSDN主页 愚润泽


69. x 的平方根

在这里插入图片描述

个人解

思路:

  • 确定答案区间:[0, min(x, 46341)](46341 是 2^31 - 1 的平方根 + 1)
  • 问题转换成:找平方 <= x 的右端点(因为题目向下取整)
  • mid选取判断:因为向下取整,left = mid会死循环

用时:
屎山代码:

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

时间复杂度:O(n)
空间复杂度:O(1)


35. 搜索插入位置

在这里插入图片描述

个人解

思路:

  • 题目意思:有target就返回target,没有就返回 >target 的第一个位置
  • 总结一下:就是返回 >= target 的第一个位置
  • 细节处理:模拟三种情况:1,正常找到target或者正常在数组中间插入;2,全部值都小于target;3,全部值都大于target(在这道题中要特殊处理全小于target的情况)

用时:5:00
屎山代码:

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;
        }
        // 特殊处理全小于target的情况
        if(nums[right] >= target)
            return right;
        else
            return nums.size();
    }
};

时间复杂度:O(logN)
空间复杂度:O(1)


852. 山脉数组的峰顶索引

在这里插入图片描述

个人解

思路:

  • 二段性:每次和右边的数比较
  • 右边的数不存在 / 右边的数 <= 当前数:山峰肯定在 [left, mid]
  • 右边的数 > 当前数:山峰肯定在 [mid + 1, right]

用时:5:00
屎山代码:

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

时间复杂度:O(logN)
空间复杂度:O(1)


162. 寻找峰值

在这里插入图片描述
在这里插入图片描述

个人解

思路:

  • 这道题的提示3:nums[i] != nums[i + 1],太重要了(保证了一定有一个峰值)
  • 每次和右侧数字比较
  • > 右侧的数字:峰值一定在[left ,mid](mid有可能是峰值)
  • < 右侧的数字:峰值一定在[mid + 1, right]

用时:10:00(一开始没看到提示3)
屎山代码:

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 right;
    }
};

时间复杂度:O(logN)
空间复杂度:O(1)


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

个人解

思路:

  • 利用数组的有序性二分,找最小
  • 但是问题在于:这里在翻转以后可能出现两段数组!一段数组上查找的时候,要判断最小值会不会出行在另一段数组上
  • 如何判断呢?如果真的有两段数组,则第二段数组的最大值一定是nums[n-1],如果当前nums[mid] < nums[n - 1]代表在正确的数组上查找了,反之就是在错误的数组上查找了,要让left移动到右边

用时:15:00
屎山代码:

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

时间复杂度:O(log n)
空间复杂度:O(1)


优质解

看了官方题解以后想到的:
在这里插入图片描述

  • 如果翻转了,产生了两个数组,则最小值,一定在第二个数组上!!!
  • 并且,前一个数组的元素都大于第二个数组的元素
  • 那我们的比较基准就可以和最后一个值进行比较
  • > nums[n-1]:数组错了,答案一定在[mid + 1, right]
  • < nums[n-1]:答案一定在[left, mid]mid也有可能是答案

代码:

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

LCR 173. 点名

个人解

思路:

  • 因为数组是升序排序的,且学号从0开始,利用学号和下标对应的特点,找出缺失值在哪一遍
  • mid == records[mid] ,则缺失值在 [mid + 1, right]
  • mid > records[left, mid]
  • 特殊判断,最后一个同学缺席

用时:7:00
屎山代码:

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

时间复杂度:O(log n)
空间复杂度:O(1)


🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

### LeetCode上的二分查找目与教程 对于希望深入理解并练习二分查找算法的人来说,LeetCode提供了一系列高质量的问和学习资源。这些问不仅涵盖了基本概念的应用,还涉及到更复杂的场景。 #### 目推荐 1. **704. 二分查找** 这道是最基础的二分查找应用实例之一,在给定有序数组`nums`中寻找目标值`target`的位置[^1]。 2. **35. 搜索插入位置** 给定一个按升序排列的整数数组 `nums` 和一个目标值 `target`, 如果找到则返回索引;如果未找到,则返回它应该被插入到哪个位置以保持顺序不变。 3. **33. 搜索旋转排序数组** 数组原本是严格增序排列但在未知的情况下进行了若干次旋转, 要求在一个这样的序列里高效地定位特定数值的存在与否及其下标。 4. **81. 搜索旋转排序数组 II** 类似于上述第3项提到的问设置,但是允许重复元素存在,增加了问难度。 5. **153. 寻找旋转排序数组中的最小值** 对于经过一次随机翻转后的单调递增列表而言,快速找出其中最小的那个数字是一项挑战性的任务。 6. **154. 寻找旋转排序数组中的最小值 II** 同样是在含有重复项目的条件下完成上一个问的要求,这无疑提高了逻辑设计方面的考量层次。 #### 教程链接 除了实际操作外,理论知识同样重要。为了帮助用户更好地掌握这些技能,官方文档以及社区贡献者们提供了丰富的指南文章: - 官方标签页下的“Binary Search”专题页面包含了精心挑选出来的入门级至高级别的各类案例分析; - 讨论区内的帖子经常会有经验分享和技术交流的机会,可以从中获取更多实战技巧和心得感悟。 ```python def binary_search(nums, target): low, high = 0, len(nums) - 1 while low <= high: mid = (low + high) // 2 if nums[mid] == target: return mid elif nums[mid] < target: low = mid + 1 else: high = mid -1 ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愚润泽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值