【优选算法必刷100题】第023~24题(二分查找算法):寻找/搜索旋转排序数组中的最小值、点名(缺失的数字)

【优选算法必刷100题】第023~24题(二分查找算法):寻找/搜索旋转排序数组中的最小值、点名(缺失的数字)

🔥艾莉丝努力练剑:个人主页

专栏传送门:《C语言》《数据结构与算法》C/C++干货分享&学习过程记录Linux操作系统编程详解笔试/面试常见算法:从基础到进阶

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬艾莉丝的简介:


🎬艾莉丝的算法专栏简介:


目录

​023  寻找/搜索旋转排序数组中的最小值

1.1  解法一:暴力查找

1.2  解法二:二分查找

1.2.1  算法思路

1.2.2  算法实现

1.2.3  尝试:选择A点呢?

1.3  博主手记

​024  点名(缺失的数字)

2.1  解法一:暴力遍历找结果

2.2  解法二:用哈希表解决

2.3  解法三:位运算

2.4  解法四:用(数学)高斯求和公式解决

2.5  解法五:二分查找(最优解)

2.5.1  算法思路

2.5.2  算法实现

2.6  五种方法总结

2.7  博主手记

结尾


​023  寻找/搜索旋转排序数组中的最小值

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

力扣题解链接:二分查找模版解决【寻找旋转排序数组中的最小值】

题目描述:

1.1  解法一:暴力查找

关于暴力查找,只需遍历一遍数组,这里不再赘述。

1.2  解法二:二分查找

1.2.1  算法思路

题目中的数组规则如下图所示——

这个C点就是我们要求的点。

二分的本质:找到一个判断标准,使得查找区间能够一分为二——二段性。

通过图像我们可以发现,[A , B]区间内的点都是严格大于点的值的,C点的值是严格小于D点的值的。但是当[C , D]区间只有一个元素的时候,C点的值是可能等于D点的值的。

因此,初始化左右两个指针left,right。

然后根据mid的落点,我们可以这样划分下一次查询的区间——

(1)当mid在[A , B]区间的时候,也就是mid位置的值严格大于D点的值,下一次查询区间在[mid + 1 , right]上;
(2)当mid在[C , D]区间的时候,也就是mid位置的值严格小于等于D点的值,下次查询区间在[left , mid]上。
当区间长度变成1的时候,就是我们要找的结果。

1.2.2  算法实现

代码演示如下——

// D点 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]; // 返回数组中的最小元素 } };
时间复杂度:O(logn),空间复杂度:O(1)。

1.2.3  尝试:选择A点呢?

A点也可以有二段性,我们可以尝试一下——

int y = nums[left]; // 选择第一个元素作为参考点 while(left < right) { int mid = left + (right - left + 1) / 2; if(nums[mid] >= y) right = mid - 1; // 还在左半部分,往右找 else left = mid; // 已经在右半部分,往左找 }
问题:当数组没有旋转时(如[1 , 2 , 3 , 4 , 5]),所有元素都 >= nums[0],会导致一直执行 right = mid - 1,最终返回错误的结果。

1.3  博主手记

本题整个的思路、算法原理、解题过程博主在纸上推导了一遍,大家可以参考一下手记的推导过程!最好做题的过程中自己也推导一遍!!!自己能够推导很重要!


​024  点名(缺失的数字)

力扣链接:LCR 173. 点名

力扣题解链接:二分查找模版解决【点名】

题目描述:

2.1  解法一:暴力遍历找结果

代码演示如下——

class Solution { public: int takeAttendance(vector<int>& records) { int n = records.size(); for (int i = 0; i < n; i++) { if (records[i] != i) { return i; } } return n; // 如果前面都连续,缺失的就是最后一个 } };
时间复杂度:O(n),空间复杂度:O(1)。

2.2  解法二:用哈希表解决

代码演示如下——

class Solution { public: int takeAttendance(vector<int>& records) { unordered_set<int> recordSet(records.begin(), records.end()); int n = records.size(); for (int i = 0; i <= n; i++) { if (recordSet.find(i) == recordSet.end()) { return i; } } return -1; } };
时间复杂度:O(n),空间复杂度:O(n)。

2.3  解法三:位运算

代码演示如下——

class Solution { public: int takeAttendance(vector<int>& records) { int result = 0; int n = records.size(); // 将 0~n 的所有数与数组中的数进行异或 for (int i = 0; i <= n; i++) { result ^= i; } for (int num : records) { result ^= num; } return result; } };
时间复杂度:O(n),空间复杂度:O(1)。

2.4  解法四:用(数学)高斯求和公式解决

代码演示如下——

class Solution { public: int takeAttendance(vector<int>& records) { int n = records.size(); // 计算 0~n 的完整和 int totalSum = n * (n + 1) / 2; // 计算实际数组的和 int actualSum = 0; for (int num : records) { actualSum += num; } // 缺失的数 = 完整和 - 实际和 return totalSum - actualSum; } };
时间复杂度:O(n),空间复杂度:O(1)。

2.5  解法五:二分查找(最优解)

2.5.1  算法思路

关于这道题中,时间复杂度为O(N) 的解法有很多种,而且也是比较好想的,就是上面我们已经展示过的四种方法,已经介绍过了,这里就不再赘述。

在这个升序的数组中,我们发现——

(1)在第一个缺失位置的左边,数组内的元素都是与数组的下标相等的;

(2)在第一个缺失位置的右边,数组内的元素与数组下标是不相等的。

因此,我们可以利用这个【二段性】,来使用【二分查找】算法。

2.5.2  算法实现

代码演示如下——

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; } };
时间复杂度:O(logn),空间复杂度:O(1)。

2.6  五种方法总结

方法时间复杂度空间复杂度适用场景
哈希表O(n)O(n)通用解法
直接遍历O(n)O(1)简单直观
位运算O(n)O(1)无溢出风险
高斯求和O(n)O(1)数学思维
二分查找O(log n)O(1)最优解

推荐使用二分查找,因为它的时间复杂度最优,且能充分利用数组有序的特性。

2.7  博主手记

本题整个的思路、算法原理、解题过程博主在纸上推导了一遍,大家可以参考一下手记的推导过程!最好做题的过程中自己也推导一遍!!!自己能够推导很重要!


结尾

往期回顾:

【优选算法必刷100题】第021~22题(二分查找算法):山脉数组的峰顶索引、寻找峰值

结语:都看到这里啦!请大佬不要忘记给博主来个“一键四连”哦!

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡

૮₍ ˶ ˊ ᴥ ˋ˶₎ა


Read more

Git 分支管理完全指南:从基础到团队协作

Git 分支管理完全指南:从基础到团队协作

🔥个人主页:Cx330🌸 ❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》 《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔 《Git深度解析》:版本管理实战全解 🌟心向往之行必能至 🎥Cx330🌸的简介: 目录 前言: 一、为什么要分支?——分支的意义 二. Git 分支基础:核心概念与常用命令 2.1 分支与 HEAD 指针解析 2.2 基础指令:查看、创建、切换分支 三. Git 分支进阶:合并、删除和冲突 3.1 合并分支(git merge 分支名) 3.2 删除分支(

By Ne0inhk

EpicDesigner 终极配置指南:快速上手专业级低代码设计器

EpicDesigner 终极配置指南:快速上手专业级低代码设计器 【免费下载链接】epic-designer 项目地址: https://gitcode.com/gh_mirrors/ep/epic-designer EpicDesigner 是一个革命性的 Vue3 低代码设计器,专为追求开发效率的现代开发者打造。无论你是前端新手还是资深工程师,都能通过这个强大的工具快速构建复杂的表单和页面布局,将开发时间从数小时缩短到几分钟。 🚀 为什么选择 EpicDesigner? 在当今快节奏的开发环境中,时间就是最宝贵的资源。EpicDesigner 通过直观的拖拽界面和智能配置系统,让你告别重复编码的烦恼。它不仅仅是一个设计工具,更是提升团队协作效率和项目交付速度的秘密武器。 核心优势一览 * 零学习曲线: 即使没有前端经验,也能快速上手 * 多UI库兼容: 无缝支持主流UI框架 * 实时预览: 所见即所得的设计体验 * 高度可扩展: 支持自定义组件和插件开发 📦 极速启动方案 环境准备 确保你的开发环境满足以下要求: * Node.js 14.x

By Ne0inhk

无人机地面站QGC的安装(ubuntu20.04)

无人机地面站QGC的安装(ubuntu20.04) 1.安装依赖 使用以下命令: sudo usermod -a -G dialout $USER sudo apt-get remove modemmanager -y sudo apt install gstreamer1.0-plugins-bad gstreamer1.0-libav gstreamer1.0-gl -y sudo apt install libfuse2 -y sudo apt install libxcb-xinerama0 libxkbcommon-x11-0 libxcb-cursor0 -y 2.下载安装包 可以直接去官网下载,链接地址:https://docs.qgroundcontrol.com/master/en/qgc-user-guide/

By Ne0inhk
从社死边缘拯救我:用 AR 眼镜打造“亲戚称呼助手“

从社死边缘拯救我:用 AR 眼镜打造“亲戚称呼助手“

从社死边缘拯救我:用 AR 眼镜打造"亲戚称呼助手 一个真实的新年灾难 大年初二,我跟着新婚妻子回娘家。 刚进门,七大姑八大姨就围了上来。一位头发花白的阿姨笑盈盈地递过来一个红包,我脑子里嗡的一声——这到底是妻子的哪位亲戚?大姨?小姨?还是什么远房表姑? “小张啊,还认识我不?” 我支支吾吾半天,最后还是妻子打了圆场:“这是大姨,小时候还抱过你呢!” 那一刻,我看到了大姨眼里的失望。这种社死现场,相信很多人都经历过:春节期间,走亲访友是必修课,但那些一年见一次的亲戚,名字和称呼根本记不住。尤其是刚结婚的新人、不常回家的打工人,简直是"称呼灾难"高发人群。 回家后,我下定决心:明年春节,我绝不能再叫错人。 思路:为什么是 AR 眼镜? 解决方案无非几种: ● 记在手机备忘录:掏手机、解锁、

By Ne0inhk