Day 1 二分算法(C++)

news2024/11/18 16:25:34

算法简介

二分查找(Binary Search)是一种常见的查找算法,它适用于已经排序好的数组或列表。它的基本思想是不断地将待查找区间分成两半,并通过比较目标值与中间元素的大小关系来确定目标值在哪一半中,从而缩小查找范围。这个过程一直重复,直到找到目标值或者确定目标值不存在为止。

基本条件:有序无重复

基本步骤:

  1. 初始化左右边界,通常为数组的起始和结束索引。
  2. 在循环中,计算中间位置索引,以及中间位置的值。
  3. 根据中间位置的值与目标值的大小关系,缩小搜索范围:
    • 如果中间值等于目标值,则找到了目标值,返回结果。
    • 如果中间值大于目标值,则说明目标值在左半部分,将搜索范围缩小为左半部分。
    • 如果中间值小于目标值,则说明目标值在右半部分,将搜索范围缩小为右半部分。
  4. 重复步骤 2 和步骤 3,直到找到目标值或者搜索范围为空。

常见的二分算法有两种写法:左闭右闭左闭右开

左闭右闭

int binarySearch(vector<int>& nums, int target) {
    int left = 0;
    int right = nums.size() - 1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid; // 找到目标值,返回索引
        } else if (nums[mid] < target) {
            left = mid + 1; // 缩小搜索范围为右半部分
        } else {
            right = mid - 1; // 缩小搜索范围为左半部分
        }
    }
    
    return -1; // 搜索范围为空,未找到目标值
}

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

左闭右开

最右边的不包含在内,所以注意三点

  • 循环条件:left == rifght无意义了
  • 初始化的时候right无需-1
  • 目标在在右边区间的时候,只需要更新right为middle就可以,因为本身就不包含
int binarySearch(vector<int>& nums, int target) {
    int left = 0;
    int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
    while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
        int middle = left + ((right - left) >> 1);
        if (nums[middle] > target) {
            right = middle; // target 在左区间,在[left, middle)中
        } else if (nums[middle] < target) {
            left = middle + 1; // target 在右区间,在[middle + 1, right)中
        } else { // nums[middle] == target
            return middle; // 数组中找到目标值,直接返回下标
        }
    }
    // 未找到目标值
    return -1;
}

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

刷题

搜索插入位置

力扣题目链接
搜索插入位置
左闭右开
先贴代码

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

当循环结束时,说明目标值不存在于数组中,此时 left 表示目标值应该插入的位置。因为 left 表示第一个大于目标值的元素的索引,所以返回 left 即可。
我们对left进行更新的时候,都保证了更新后left左边的元素比目标值小,最后left == right,而right及其右边的元素肯定比目标值大
左闭右闭
先贴代码

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1; // 初始化右边界为数组最后一个元素的索引

        while (left <= right) { // 使用左闭右闭的循环条件
            int middle = left + (right - left) / 2; // 计算中间位置的索引
            if (nums[middle] == target) {
                return middle; // 找到目标值,返回索引
            } else if (nums[middle] < target) {
                left = middle + 1; // 缩小搜索范围为右半部分
            } else {
                right = middle - 1; // 缩小搜索范围为左半部分
            }
        }

        return left; // 搜索范围为空,未找到目标值,返回插入位置
    }
};

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

力扣题目链接

在排序数组中查找元素的第一个和最后一个位置
先贴代码

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int lb = getLeftBorder(nums, target);
        int rb = getRightBorder(nums, target);
        if(lb == -1 || rb == -1){
            return {-1, -1};
        }
        else{
            return {lb, rb};
        }
    }
    int getLeftBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int leftborder = -1;
        while(left <= right){
            int middle = (left + right) / 2;
            if(nums[middle] > target){
                right = middle - 1;
            }
            else if(nums[middle] < target){
                left = middle + 1;
            }
            else {
                right = middle - 1;
                leftborder = middle;
            }
        }
        return leftborder;
    }
    int getRightBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int rightborder = -1;
        while(left <= right){
            int middle = (left + right) / 2;
            if(nums[middle] > target){
                right = middle - 1;
            }
            else if(nums[middle] < target){
                left = middle + 1;
            }
            else {
                left = middle + 1;
                rightborder = middle;
            }
        }
        return rightborder;
    }
};

这个题目对时间复杂度有要求,思考后发现可以使用两个二分查找的算法分别查找两个分别查找两个边界,我们只需要分析出来一个边界的函数就可以了
普通情况下我们正常处理,我们需要考虑的是nums[middle]==target,此时如果查找左边界,先标记一下,不然后面推出了通过left不好进行计算,我们更新right到middle前面

x 的平方根

力扣题目链接
x 的平方根

class Solution {
public:
    int mySqrt(int x) {
        if (x == 0 || x == 1) {
            return x;
        }
        int left = 1;
        int right = x;
        int ans = 0;
        while (left <= right) {
            int middle = left + (right - left) / 2;
            if (middle <= x / middle) { // 判断条件改为小于等于,避免溢出
                ans = middle;
                left = middle + 1;
            } else {
                right = middle - 1;
            }
        }
        return ans;
    }
};

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

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

相关文章

【leetcode热题】 位1的个数

编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位数为 1 的个数&#xff08;也被称为汉明重量&#xff09;。 提示&#xff1a; 请注意&#xff0c;在某些语言&#xff08;如 Java&#xff09;中…

2024.03.23 健身打卡第 34 天

君之英才&#xff0c;实乃盖世无双&#xff0c;渴求统一大业却属鲲鹏之志。然涓滴之水汇成江河&#xff0c;已属不易&#xff0c;奔流向前&#xff0c;汇入大海之时&#xff0c;更会倍感自身之渺茫。 2024.03.23 健身打卡第 34 天

架构整洁之道-读书总结

1 概述 1.1 关于本书 《架构整洁之道》&#xff08;Clean Architecture: A Craftsman’s Guide to Software Structure and Design&#xff09;是由著名的软件工程师Robert C. Martin&#xff08;又称为Uncle Bob&#xff09;所著。这本书提供了软件开发和架构设计的指导原则…

王道C语言督学营OJ课后习题(课时14)

#include <stdio.h> #include <stdlib.h>typedef char BiElemType; typedef struct BiTNode{BiElemType c;//c 就是书籍上的 datastruct BiTNode *lchild;struct BiTNode *rchild; }BiTNode,*BiTree;//tag 结构体是辅助队列使用的 typedef struct tag{BiTree p;//树…

文件包含一-WEB攻防-PHP应用文件包含LFIRFI伪协议编码算法无文件利用黑白盒

演示案例&#xff1a; 文件包含-原理&分类&利用&修复黑盒利用-VULWEB-有无包含文件白盒利用-CTFSHOW-伪协议玩法 #文件包含-原理&分类&利用&修复 1、原理 程序开发人员通常会把可重复使用的函数写到单个文件中&#xff0c;在使用某些函数时&#xff0c…

MySQL---视图

目录 一、介绍 二、语法 三、视图的更新 四、视图作用 一、介绍 视图&#xff08;View&#xff09;是一种虚拟存在的表。视图中的数据并不在数据库中实际存在&#xff0c;行和列数据来自定义视图的查询中使用的表&#xff0c;并且是在使用视图时动态生成的。 通俗的讲&#…

CSS时钟案例

文章目录 1. 演示效果2. 分析思路3. 代码实现 1. 演示效果 2. 分析思路 背景是表盘&#xff0c;不用自己制作然后用CSS的定位做时针&#xff0c;分针和秒针黑点用伪元素::after生成转动用animation实现 3. 代码实现 <!DOCTYPE html> <html lang"en">&…

密码学之哈希碰撞和生日悖论

哈希碰撞 哈希碰撞是指找到两个不一样的值&#xff0c;它们的哈希值却相同 假设哈希函数的取值空间大小为k &#xff0c;计算次数为n 先算每个值不一样的概率P’ 所以至少两个值相同(即存在哈希碰撞)的概率P为 生日悖论 假设班里有50个人&#xff0c;求班里至少两个人相同…

java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics

今天在使用springBoot连接influxdb报错 java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics 详细报错如下&#xff0c;提出我们缺少一个依赖 原因是由于创建influxdb客户端缺少Kotlin运行时库 解决办法就是 1.显示的添加okhttp的依赖 <dependency>…

如何用VSCode和Clangd与Clang-Format插件高效阅读Linux内核源码及写驱动

一、如何高效阅读Linux源码&#xff1a;基于clangd uboot/busybox等都可以用这种方式&#xff0c;理论上说所有基于Make和Cmake的源码工程都可以用这套方案 阅读Linux源码最大问题在于调用链太复杂&#xff0c;一个函数或变量引用太多&#xff0c;source insight和cscope等基于…

Uibot6.0 (RPA财务机器人师资培训第1天 )RPA+AI、RPA基础语法

训练网站&#xff1a;泓江科技 (lessonplan.cn)https://laiye.lessonplan.cn/list/ec0f5080-e1de-11ee-a1d8-3f479df4d981(本博客中会有部分课程ppt截屏,如有侵权请及请及时与小北我取得联系~&#xff09; 紧接着小北之前的几篇博客&#xff0c;友友们我们即将开展新课的学习~…

Nacos部署(三)Docker部署Nacos2.3单机环境

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; Nacos部署&#xff08;三&#xff09;Docker部署Nacos2.3单机环境 ⏱️…

Python数据结构实验 队列的实现

一、实验目的 1&#xff0e;掌握用Python定义队列的顺序存储结构和链式存储结构&#xff0c;以便在实际背景下灵活运用&#xff1b; 2&#xff0e;掌握队列的特点&#xff0c;即先进先出的原则&#xff1b; 3&#xff0e;掌握队列的基本操作实现方法。 二、实验环境 1&…

BufferedInputStream详解

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java之IO流啦&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好习惯&am…

javase day10笔记

第十天课堂笔记 debug调试★★★ 步骤: 设置断点 - > 启动调试debug -> 单步运行 -> 观察参数 单步跳过f8: 向下执行语句,不进入方法内部单步跳入f7: 进入方法内部执行单步跳出shift f8: 跳出当前方法,到方法调用处跳转到光标所在的位置alt f9: 变量整合 变量 …

【力扣hot100】1. 两数之和 49.字母异位词分组 128. 最长连续序列

目录 1. 两数之和题目描述做题思路参考代码 49.字母异位词分组题目描述做题思路参考代码 128. 最长连续序列题目描述做题思路参考代码 1. 两数之和 题目描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数…

亚马逊云科技《生成式 AI 精英速成计划》

最近亚马逊云科技推出了「生成式AI精英速成计划」&#xff0c;获取包含&#xff1a;免费学习热门生成式AI课程、技能证书、人力主管的面试辅导、云计算国际认证、免费去往北美参加全球用户大会等&#xff5e; 针对开发者和企业非技术专业人士&#xff0c;了解如何使用大模型平台…

【机器学习之---统计】统计学基础概念

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 统计学基础 1. 频率派 频率学派&#xff08;传统学派&#xff09;认为样本信息来自总体&#xff0c;通过对样本信息的研究可以合理地推断和估计总体信息…

怎么拆解台式电脑风扇CPU风扇的拆卸步骤-怎么挑

今天我就跟大家分享一下如何选购电脑风扇的知识。 我也会解释一下机箱散热风扇一般用多少转。 如果它恰好解决了您现在面临的问题&#xff0c;请不要忘记关注本站并立即开始&#xff01; 文章目录列表&#xff1a;大家一般机箱散热风扇都用多少转&#xff1f; 机箱散热风扇选择…

PieCloudDB Database 3.0 正式发布丨数仓虚拟化流转数据要素

3月14日&#xff0c;拓数派 2024 年度战略暨新产品发布会在上海国际会议中心成功举行。本次大会的主题为「数仓虚拟化 流转数据要素」&#xff0c;吸引了众多业内资深专家和合作伙伴参与&#xff0c;共同探讨数据要素流转和数字技术创新等热门话题。 拓数派创始人兼 CEO 冯雷&…