当前位置: 首页 > news >正文

二分查找算法(8) _点名

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

二分查找算法(8) _点名

收录于专栏【经典算法练习】
本专栏旨在分享学习算法的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

温馨提示:

1. 题目链接 :

2. 题目描述 :

3. 解法 : 

1. 哈希表

    算法思路 :

    代码展示 :

    结果分析 :

2. 暴力遍历

    算法思路 :

    代码展示 :

    结果分析 :

3. 位运算

    算法思路 :

    代码展示 :

    结果分析 :

4. 数学(高斯求和公式)

    算法思路 :

    代码展示 :

    结果分析 :

5. 二分查找算法

    算法思路 :

    代码展示 :

    结果分析 :

4. 二分算法模板总结


温馨提示:

这道题是直接使用了二分模板,轻松拿捏了这道题,如果你还不知道的话,自行去下篇博客

---二分查找算法(2) _在排序数组中查找元素的第一个和最后一个_模板-CSDN博客

1. 题目链接 :

OJ链接: 点名

2. 题目描述 :

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

示例 1:

输入: records = [0,1,2,3,5]
输出: 4

示例 2:

输入: records = [0, 1, 2, 3, 4, 5, 6, 8]
输出: 7

提示:

1 <= records.length <= 10000

3. 解法 : 

1. 哈希表

    算法思路 :

算法步骤
1. 初始化数据结构:

        使用 unordered_map<int, int>来记录数组中每个元素的出现次数。
2. 遍历记录:

        遍历给定的 records 数组,对于每个元素 records[i],将其作为键存入哈希表,并将对应的值(计数)加 1。这样可以快速跟踪数组中每个元素的出现情况。
3. 查找缺失的最小值:

        从 0 开始遍历到 records.size()(包括 size,这是因为缺失的数字可以是等于数组长度的情况)。
        对于每个 i,检查哈希表中是否存在 i 作为键。
        如果 hash[i] 的值为 0,则表示 i 在数组中缺失,立即返回 i。
4. 返回最后的结果:

        如果在 0 到 records.size() 的所有整数中都存在,返回 records.size(),表示下一个缺失的非负整数就是数组的大小。

    代码展示 :

class Solution {
public:int takeAttendance(vector<int>& records) {unordered_map<int, int> hash;for(int i = 0; i < records.size(); i++)hash[records[i]]++;for(int i = 0; i <= records.size(); i ++)if(hash[i] == 0) return i;return 0;}
};

 

    结果分析 :

时间复杂度
O(n):遍历数组一次来构建哈希表,再遍历 0 到 records.size() 进行查找。总的时间复杂度是线性的。
空间复杂度
O(n):在最坏情况下,哈希表需要存储数组中的所有元素。

2. 暴力遍历

    算法思路 :

思路
外层循环:从 0 开始,逐渐检查每个非负整数 i。
内层循环:遍历 records 数组,检查 i 是否存在。
找不到则返回:如果内层循环没有找到 i,则立即返回 i。

    代码展示 :

class Solution {
public:int takeAttendance(vector<int>& records) {for(int i = 0; ; i++){bool found = false;for(int j = 0; j < records.size(); j++){if(records[j] == i){found = true;break;}}if(!found) return i;}return -1;}
};

 

    结果分析 :

时间复杂度
O(n ^ 2):最坏情况下需要检查所有 n 个元素多次,因此时间复杂度为 O(n²),在数组很大时效率较低。
空间复杂度
O(1):没有使用额外的数据结构,只是使用了常量空间。

3. 位运算

    算法思路 :

算法流程
1. 初始化:

使用变量 ret 初始化为 0,用于存储最终结果。
2. 第一轮循环:

遍历 0 到 n 的所有整数(包括 n)。
将所有整数进行异或操作。
3. 第二轮循环:

遍历输入数组 records 中的元素(从 0 到 n - 1)。
将所有元素进行异或操作。
4. 返回结果:

最后,ret 中的值就是缺失的最小非负整数。

    代码展示 :

class Solution {
public:int takeAttendance(vector<int>& records) {int n = records.size();int ret = 0;for(int i = 0; i <= n; i++){ret ^= i;}for(int i = 0; i <= n - 1; i++){ret ^= records[i];}return ret;}
};

 

    结果分析 :

复杂度分析
时间复杂度:O(N),算法中有两个循环,各遍历 n 次。
空间复杂度:O(1),只使用了常量级的额外空间。
关键点
异或的特性:相同的数异或为 0,不同的数异或为 1,因此最后的结果是缺失的数字。
范围:由于 records 中的值在 0 到 n - 1 之间,最终的异或结果会得出缺失的最小非负整数。

4. 数学(高斯求和公式)

    算法思路 :

算法流程
初始化:

变量 ret 初始化为从 0 到 n(records.size())的和,使用公式 n * (n + 1) / 2。这里 n 是 records.size()。
遍历输入数组:

使用范围循环遍历 records 中的每个元素,将其值从 ret 中减去。
返回结果:

最终,ret 中的值就是缺失的最小非负整数。

    代码展示 :

class Solution {
public:int takeAttendance(vector<int>& records) {int ret = records.size() * (records.size() + 1) / 2;for(auto ch : records)ret -= ch;return ret;}
};

 

    结果分析 :

复杂度分析
时间复杂度:O(N),算法中有一个循环遍历 records,因此整体复杂度是线性的。
空间复杂度:O(1),只使用了常量级的额外空间。
关键点
数学求和:通过计算 0 到 n 的总和,快速得到应有的总和。
缺失的整数:通过减去实际存在的记录值,最终得到缺失的整数。

5. 二分查找算法

    算法思路 :

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

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

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

因此, 我们可以利用这个[二段性], 来使用[二分查找]算法.

    代码展示 :

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) right = mid;else left = mid + 1;}if(records[left] == left) return left + 1;else return records[left] - 1;}
};

1. 初始化:

        定义两个指针 left 和 right,分别指向 0 和 records.size() - 1。
2. 二分搜索:

        进入 while 循环,直到 left 小于 right。
        计算 mid 为(left + (right - left) / 2),避免溢出。
        检查 records[mid] 是否等于 mid:
                如果 records[mid] 不等于 mid,说明在 mid 左侧的元素缺失,因此将 right 移动到 mid。
                如果 records[mid] 等于 mid,说明在 mid 右侧的元素未缺失,因此将 left 移动到 mid + 1。
3. 返回结果:

        循环结束后,left 将指向第一个缺失的数或最后一个不缺失的数。
        通过检查 records[left] 与 left 的关系:
                如果 records[left] 等于 left,说明缺失的数字是 left + 1。
                否则,返回 records[left] - 1,这表示缺失的数字在 left 的左侧。

 

 

    结果分析 :

复杂度分析
时间复杂度:O(log N),由于使用了二分搜索,每次搜索都将查找范围减半。
空间复杂度:O(1),只使用了常量级的额外空间。
关键点
有序性:算法假设输入数组是已排序的,这对于二分搜索是必要的前提。
边界条件:需要确保涉及数组索引时不越界,并正确处理缺失数字的位置。

4. 二分算法模板总结

 这里再次强调一下我们的二分算法模板(真的很好用!!!)     

while(left < right){int mid = left + (right - left) / 2;if(...) left = mid + 1;else right = mid;
}while(left < right){int mid = left + (right - left + 1) / 2;if(...) left = mid;else right = mid - 1;
}

快速记忆:

分类讨论的代码,就题论题即可

下面出现-1,上面就+1,否侧不加


http://www.mrgr.cn/news/35193.html

相关文章:

  • 自顶向下逐步求精解决LeetCode第3307题找出第K个字符II题
  • C语言第13节:指针(3)
  • STM32芯片EXIT外部中断的配置与原理以及模板代码(标准库)
  • Git学习教程(更新中)
  • 云原生学习
  • I.MX6U 裸机开发9.BEEP蜂鸣器实验
  • Solidity——抽象合约和接口详解
  • 【python】数据类型
  • WebRtc实际应用
  • 【数学二】极限的计算- 等价无穷小替换、洛必达法则求极限
  • 找不到MFC140.dll无法继续执行代码怎么办,共有6种解决方法
  • 离线一机一码验证和网络验证的区别以及使用场景
  • Figma 中要放大并下载 UI 设计中的图标
  • 如何利用 Kafka,实时挖掘企业数据的价值?
  • 基于Ambari搭建大数据分析平台(30分钟速成)全网最全最详细的Ambari搭建大数据分析平台:
  • (13)mysql慢查询常用语句
  • 船只类型识别系统源码分享
  • 月考成绩发布步骤-易查分
  • 异云双活实践案例
  • 【Docker】如何让docker容器正常使用nvidia显卡
  • 大数据Flink(一百二十四):案例实践——淘宝母婴数据加速查询
  • CaLM 因果推理评测体系:如何让大模型更贴近人类认知水平?
  • 英码科技亮相华为全联接大会2024,携手共赢行业智能化
  • Mapbox封装图形绘制工具 线,圆,polygon,删除,点 mapbox-gl-draw-circle mapbox-gl-draw
  • Pytorch实现Transformer
  • 用OpenSSL搭建PKI证书体系