【二分查找】锦集

news2024/11/13 10:45:52

二分查找锦集

  • 二分前言
  • 1. 二分查找
    • 1.1 题目来源
    • 1.2 题目描述
    • 1.3 代码展示
  • 2. 在排序数组中查找元素的第一个和最后一个位置
    • 2.1 题目来源
    • 2.2 题目描述
    • 2.3 解题分析
  • 3. 搜索插入位置
    • 3.1 题目来源
    • 3.2 题目描述
    • 3.3 解题分析
  • 4. x 的平方根
    • 4.1 题目来源
    • 4.2 题目描述
    • 4.3 解题分析
  • 5. 山脉数组的峰顶索引
    • 5. 1 题目来源
    • 5.2 题目描述
    • 5.3 题目解析
  • 6. 寻找峰值
    • 6. 1 题目来源
    • 6.2 题目描述
    • 6.3 题目解析
  • 7. 寻找旋转排序数组中的最小值
    • 7. 1 题目来源
    • 7.2 题目描述
    • 7.3 题目解析
  • 8. LCR 173. 点名
    • 8. 1 题目来源
    • 8.2 题目描述
    • 8.3 题目解析

二分前言

一般我们使用二分查找的时候是会使用到两个模板的。

int search(vector<int>& nums) 
{
	int left = 0, right = nums.size() - 1;
	while (left < right)
	{
		int mid = left + (right - left) / 2;
		if (chick()) left = mid + 1;
		else right = mid;
	}
}

另一种就是

int search(vector<int>& nums) 
{
	int left = 0, right = nums.size() - 1;
	while (left < right)
	{
		int mid = left + (right - left + 1) / 2;
		if (chick()) right = mid - 1;
		else left = mid;
	}
}

上面两种最明显的区别就是求mid的时候,第一种是直接进行left + (right - left) / 2;找中间值的,而第二种确实left + (right - left + 1) / 2;进行求中间值的,而至于为什么需要进行加一后在求中间值我们,我们简单来分析一下。

在这里插入图片描述

所以我们其实可以得到一个结论,就是第一种模板其实是又向左收敛的趋势,而第二种是有向右的收敛趋势的。

1. 二分查找

1.1 题目来源

704. 二分查找

1.2 题目描述

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

  1. 示例 1:
    输入: nums = [-1,0,3,5,9,12], target = 9
    输出: 4
    解释: 9 出现在 nums 中并且下标为 4
  2. 示例 2:
    输入: nums = [-1,0,3,5,9,12], target = 2
    输出: -1
    解释: 2 不存在 nums 中因此返回 -1

1.3 代码展示

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

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

2.1 题目来源

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

2.2 题目描述

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

  1. 示例 1:
    输入:nums = [5,7,7,8,8,10], target = 8
    输出:[3,4]
  2. 示例 2:
    输入:nums = [5,7,7,8,8,10], target = 6
    输出:[-1,-1]
  3. 示例 3:
    输入:nums = [], target = 0
    输出:[-1,-1]

0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109

2.3 解题分析

这里的要求是找到第一个出现target的位置,和最后一次出现target的位置。而这里其实我们可以利用二分两种模板的特性,也就是我们一开始讲的。第一种模板右向右收敛的趋势,可以利用这一点找到第一target出现的位置,第二种模板右向左收敛的趋势,可以利用这点找到最后一个出现target的位置。

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) 
    {
        // 边界处理
        if (nums.size() == 0) return {-1,-1};
        int left = 0, right = nums.size() - 1;
        int begin = -1, end = -1;

        // 找第一个target出现的位置
        while (left < right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target)
                left = mid + 1;
            else right = mid;
        }
        if (nums[left] == target) begin = left;
        left = 0, right = nums.size() - 1;

        // 找最后一次出现target的位置
        while (left < right)
        {
            int mid = left + (right - left + 1) / 2;
            if (nums[mid] > target)
                right = mid - 1;
            else left = mid;
        }
        if (nums[left] == target) end = left;

        return {begin, end};
    }
};

3. 搜索插入位置

3.1 题目来源

35. 搜索插入位置

3.2 题目描述

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

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

示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
2. 示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
3. 示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4

提示:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 为 无重复元素 的 升序 排列数组
-104 <= target <= 104

3.3 解题分析

通过题目分析我们可以直到,这里我们要找的下标其实就两种情况,一种是target在nums数组中便直接返回元素的下标即可,另一种就是元素不在nums数组中,那么我们就要找到一pos位置,这个pos位置要满足nums[pos - 1] < target < nums[pos],也就是返回找到第一大于target的位置。而这里其实我们可以将等于的情况进行合并即:nums[pos - 1] < target < =nums[pos]。

在这里插入图片描述

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) 
    {
        int left = 0, right = nums.size() - 1;
        if (target > nums[right]) return right + 1; // 这里做边界处理,其实也可以直接将right=nums.size(),因为即使多出来了一元素,但这个元素不会影响结果,这里是为了迎合我们的一致性。
        while (left < right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target)
                left = mid + 1;
            else right = mid;
        }
        return left;
    }
};

4. x 的平方根

4.1 题目来源

69. x 的平方根

4.2 题目描述

给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。

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

提示:
0 <= x <= 231 -

4.3 解题分析

这一题可以说是和上一题“搜索插入位置”是相反的,上一题是找到第一个大于等于target的数据,而这一天则是找到第一个小于等于target的数据。首先我们要找到x的平方根的话,一定是y开平发要小于等于x的,而这个y就是我们要找的target。这里也可以做一下优化,也就是说x的平方根一定是会比x/2要小的,但是其实也没多大意义,因为二分其实不太在意这一点。

在这里插入图片描述

class Solution {
public:
    int mySqrt(int x) 
    {
        int left = 0, right = x / 2 + 1; // 处理x < 2 情况
        while (left < right)
        {
            long long mid = left + (right - left + 1) / 2; // 防止数据越界
            if (mid * mid > x)
                right = mid - 1;
            else left = mid;
        }
        return left;
    }
};

5. 山脉数组的峰顶索引

5. 1 题目来源

852. 山脉数组的峰顶索引

5.2 题目描述

给定一个长度为 n 的整数 山脉 数组 arr ,其中的值递增到一个 峰值元素 然后递减。
返回峰值元素的下标。
你必须设计并实现时间复杂度为 O(log(n)) 的解决方案。

  1. 示例 1:
    输入:arr = [0,1,0]
    输出:1
  2. 示例 2:
    输入:arr = [0,2,1,0]
    输出:1
  3. 示例 3:
    输入:arr = [0,10,5,2]
    输出:1

提示:
3 <= arr.length <= 105
0 <= arr[i] <= 106
题目数据 保证 arr 是一个山脉数组

5.3 题目解析

假设我们要找到峰值的小标是Imax,也就是说明了,Imax的左边是一个上升的趋势,Imax的右边是一个下降到趋势。所以我们可以得出两个结论:

  1. 当 i < Imax的时候:arr[i] < arr[i + 1];
  2. 当 i >= imax 的是偶:arr[i] >= arr[i + 1]

所以根据这个特性我们很快可以使用到我们的第一个快排模板。

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

6. 寻找峰值

6. 1 题目来源

162. 寻找峰值

6.2 题目描述

峰值元素是指其值严格大于左右相邻值的元素。
给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。
你必须实现时间复杂度为 O(log n) 的算法来解决此问题。

  1. 示例 1:
    输入:nums = [1,2,3,1]
    输出:2
    解释:3 是峰值元素,你的函数应该返回其索引 2。
  2. 示例 2:
    输入:nums = [1,2,1,3,5,6,4]
    输出:1 或 5
    解释:你的函数可以返回索引 1,其峰值元素为 2;或者返回索引 5, 其峰值元素为 6。

提示:
1 <= nums.length <= 1000
-231 <= nums[i] <= 231 - 1
对于所有有效的 i 都有 nums[i] != nums[i + 1]

6.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])
                left = mid + 1;
            else right = mid;
        }
        return left;
    }
};

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

7. 1 题目来源

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

7.2 题目描述

已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 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. 示例 1:
    输入:nums = [3,4,5,1,2]
    输出:1
    解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。
  2. 示例 2:
    输入:nums = [4,5,6,7,0,1,2]
    输出:0
    解释:原数组为 [0,1,2,4,5,6,7] ,旋转 3 次得到输入数组。
  3. 示例 3:
    输入:nums = [11,13,15,17]
    输出:11
    解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组

提示:
n == nums.length
1 <= n <= 5000
-5000 <= nums[i] <= 5000
nums 中的所有整数 互不相同
nums 原来是一个升序排序的数组,并进行了 1 至 n 次旋转

7.3 题目解析

在这里插入图片描述
我们可以将数据进行折线图化,因为题目已经严格的数组是严格遵循升序的,并且是没有重复数据的,只是进行了单独的反转而已,于是从图中我们就可以看出是由二段性的,这里我们用数组中的最后一个元素x来进行分割。在target的左侧,所有的数据到是大于x的,而在target的右侧则是小于等于x的。所以也就有了我们的第一个模板了。

至于为什么用最后一个元素来充当分割数据而不用第一个数据来充当分割数据,是因为,如果选用第一个充当分割数据的化会有边界问题,针对的就是如果一开始他就是一个递增的,那么如果选用第一个数据作为分割数据的话,找到的将会是数组中的最大值,而不是最小值。

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

8. LCR 173. 点名

8. 1 题目来源

LCR 173. 点名

8.2 题目描述

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

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

提示:
1 <= records.length <= 10000

8.3 题目解析

在这里插入图片描述
一旦mid==nums[mid]就说明当前mid之前包括mid一定是没有缺失的,就可以让left=mid+1向前的,如果不等于,就说明mid这个索引对应的可能就是缺失的,即使right=mid即可,而这个恰好对应着我们的模板一

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2115861.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Oracle rman 没有0级时1级备份和0级大小一样,可以用来做恢复 resetlogs后也可以

文档说了 full backup 不能 用于后续的level 1&#xff0c;没说level 1没有level 0 是不是level 1就是level 0&#xff1f; 1级备份变0级的原因 及 Enabling Change Tracking生效没有-CSDN博客 这个文档说明1级备份时没有找到0级就是0级备份&#xff0c;可以用来完整恢复的。…

春日美食家:SpringBoot网上订餐系统

1 绪论 1.1 研究背景 随着互联网技术的快速发展&#xff0c;网络时代的到来&#xff0c;网络信息也将会改变当今社会。各行各业在日常企业经营管理等方面也在慢慢的向规范化和网络化趋势汇合[13]。电子商务必将成为未来商务的主流&#xff0c;因此对于餐饮行业来说&#xff0c;…

51单片机的无线病床呼叫系统【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块温湿度传感器模块矩阵按键时钟模块等模块构成。适用于病床呼叫系统、16床位呼叫等相似项目。 可实现基本功能: 1、LCD1602实时显示北京时间、温湿度信息、呼叫床位等信息&#xff1b; 2、DHT11采集病房温湿度信息&…

RTMP播放器延迟最低可以做到多少?

技术背景 RTMP播放器的延迟可以受到多种因素的影响&#xff0c;包括网络状况、推流设置、播放器配置以及CDN分发等。因此&#xff0c;RTMP播放器的延迟并不是一个固定的数值&#xff0c;而是可以在一定范围内变化的。 正常情况下&#xff0c;网上大多看到的&#xff0c;针对R…

【GIS系列】通过Java代码高效实现ArcGIS SDE数据库的数据叠加分析

作者&#xff1a;后端小肥肠 &#x1f347; 我写过的文章中的相关代码放到了gitee&#xff0c;地址&#xff1a;xfc-fdw-cloud: 公共解决方案 &#x1f34a; 有疑问可私信或评论区联系我。 &#x1f951; 创作不易未经允许严禁转载。 本文涉及GDAL及GeoTools代码实践&#xff…

计算机毕业设计选题推荐-宠物店管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

机器学习(9.2-9.8)pytorch学习(二)

文章目录 摘要Abstract 1 torch 和 torchvision1.1 查看CIFAR10数据集内容1.2 Dataloader的使用 2 神经网络的构建2.1 神经网络的基本骨架2.2 卷积层原理2.2.1 卷积基本原理2.2.2 padding 2.3 构建一个卷积神经网络2.4 池化层2.5 非线性激活2.5.1 RELU的使用2.5.2 Sigmoid的使用…

【开源免费】基于SpringBoot+Vue.J大学生租房平台(JAVA毕业设计)

本文项目编号 T 019 &#xff0c;文末自助获取源码 \color{red}{T019&#xff0c;文末自助获取源码} T019&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

java基础概念21-权限修饰符、代码块

一、权限修饰符 1-1、作用 权限修饰符&#xff0c;是用来控制一个成员能够被访问的范围的。 可以修饰&#xff1a;成员变量&#xff0c;方法&#xff0c;构造方法&#xff0c;内部类。 1-2、权限修饰符的分类 二、代码块 局部代码块构造代码块静态代码块 2-1、局部代码块 …

【C++ Primer Plus习题】12.5

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream> #include <cstdlib> #in…

Linux-【组管理、权限管理、定时任务调度】

目录 前言 Linux组基本介绍 文件/目录 所有者 查看文件 所有者 修改文件所有者 文件/目录 所在组 修改文件/目录 所在组 其它组 改变用户所在组 权限的基本介绍 rwx权限 rwx作用到文件 rwx作用到目录 修改权限 第一种方式&#xff1a;、-、变更权限 第二种方式…

Java进阶13讲__第11讲

配置文件 日志 1. Properties属性文件 1.1 特点、作用 都只能是键值对键不能重复文件后缀一般是.properties结尾的 1.2 读取 package cn.hdc.oop10.properties;import java.io.FileNotFoundException; import java.io.FileReader; import java.util.Properties; import j…

「iOS」折叠cell

iOS学习 前言简单的折叠cell效果原理 稍作修改总结 前言 在暑期仿写中&#xff0c;3G share项目里我们简单的使用了折叠cell。现在写一篇博客来总结该方法。 简单的折叠cell 效果 先看效果&#xff1a; 原理 将cell的高度设置为一个单元格的高度。创建一个按钮&#xff0…

【C++】作用域指针、智能指针、共享指针、弱指针

十、智能指针、共享指针 从上篇文章 【C】如何用C创建对象&#xff0c;理解作用域、堆栈、内存分配-CSDN博客 中我们知道&#xff0c;你的对象是创建在栈上还是在堆上&#xff0c;最大的区别就是对象的作用域不一样。所以在C中&#xff0c;一旦程序进入另外一个作用域&#xf…

【xinference】(19):在L40设备上通过Xinference框架,快速部署CogVideoX-5b模型,可以生成6秒视频,速度比409D快一点

1&#xff0c;关于Xinference Xorbits Inference (Xinference) 是一个开源平台&#xff0c;用于简化各种 AI 模型的运行和集成。借助 Xinference&#xff0c;您可以使用任何开源 LLM、嵌入模型和多模态模型在云端或本地环境中运行推理&#xff0c;并创建强大的 AI 应用。 htt…

鸿蒙开发笔记_电商严选01_登录页面(静态页面)

由于上班较忙,抽空闲暇时间,快速更新中。。。 效果图 登录页面(静态页面) import CommonConstants from ./CommonConstants;/*** 登录页面*/ // 输入文本框,的自定义样式扩展 // @Extend装饰器表示继承、扩展的意思。这里代表:自定义样式扩展 @Extend(TextInput) functio…

Qt使用小技巧之按钮动态变化

前言 最近写小demo中无意发现的&#xff0c;是想实现当鼠标悬停到按钮上面的时候&#xff0c;按钮实现动态变化&#xff0c;让人知道鼠标经过了按钮&#xff0c;效果如下 hoverDynamicPushButton 正文 首先是将按钮的边框给去掉&#xff0c;然后设置下它的悬停伪状态就行了 格…

linux日常使用命令总结

一、文件复制 在 Linux 中&#xff0c;复制文件是一个常见的操作&#xff0c;通常使用 cp 命令来完成。cp 命令提供了丰富的选项来满足不同的需求。下面是使用 cp 命令复制文件的一些基本用法和示例。 基本用法 cp 命令的基本语法如下&#xff1a; cp [选项] 源文件 目标文…

京东获得JD商品详情 API 返回值说明||京东商品详情数据采集API接口详解

item_get-获得JD商品详情 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,item_search_shop等]cacheStri…

了解分布式事务与本地事物基本概念

一、本地事物 1、事物的基本性质 数据库事物的几个特性&#xff1a;原子性、一致性、隔离性、持久性&#xff0c;简称ACID&#xff1b; 原子性&#xff1a;一系列的操作整体不可拆分&#xff0c;要么全成功&#xff0c;要么同时失败。 一致性&#xff1a;数据在事物的前后&am…