二分查找的两种形式(C++实现)

news2024/11/16 11:28:07

现在有一个这样的问题需要求解

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

示例

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

继上次写完二分查找后,又在网上查看了其他资料,发现二分查找其实常用的有两种写法,这篇文章里(二分查找)只写了一种形式,而且可能介绍的不是很详细,所以这里将二分查找的两种形式做以归纳总结,方便记忆。

二分查找的两种形式的主要区别在于区间的定义,①左闭右闭[left,right],②左闭右开[left,right)

具体来说就是下面这两种表示区间的方式,虽然表现方式略有差异,但实际上表达的区间范围是一致的,殊途同归(相信大家对初中数学开闭区间的描述仍有印象) 

 

由于区间定义的微小差别,导致程序中mid变量的取值也有相应的改变(一定得改变,不然就会出错),下面我们来介绍一下这两种形式

①左闭右闭[left,right]

我们定义target是在一个左闭右闭[left,right]的区间里,那么要注意两个事情

  • while的循环条件;while(left<=right)。在while的循环条件里面就要用<= ,因为这个时候left==right是有意义的
  • if(nums[mid]>target)之后对right怎样处理?right要赋值为mid-1,因为当前这个nums[mid]一定不是target,那么接下来要查找的左区间的右边界就是mid-1

具体怎么写呢?

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

对上面的程序做简单注释

int left=0,right=nums.size()-1;

定义一个区间,左边界left=0,右边界right=nums.size()-1,实际上就是说定义了一个闭区间,要求包括整个数组的所有元素。定义target在这个区间范围里。

注意代码right=nums.size()-1,为什么要减1?因为nums.size()返回的是nums中有多少个元素,而我们又知道数组的下标是从0开始的,所以一个数组的最后一个元素的下标一定是这个数组中拥有元素的个数减1

while(left<=right)

这里为什么要写成<=号,我们之前也说过,因为当left==right时,仍然是有意义的。

int mid=(right-left)/2+left;

这里为什么要把mid写成这样?其实它和

int mid = (left+rirht)/2

是一样的,那为什么要那样写而不写成下面这样,主要原因是怕内存溢出,

int类型的整数能够表示的最大数字是2147483647 ,假如在运行过程中,left不断逼近right,直到left和right两个数都接近2147483647,他们两个再相加结果就会溢出,会变成负数,所以改用mid=(right-left)/2+left

通过上面这个小测试我们就可以想通为什么要写成 mid=(right-left)/2+left的样子了。

在这里可能大家还会碰到另外一种书写形式

int mid=((right-left)>>1)+left

这里的“>>"运算符是右移运算符,

举个例子

 

  

 所以我们可以知道 int mid=((right-left)>>1)+left 和 int mid=(right-left)/2+left; 这两种形式是等效的,但是位运算符>>比’ / ‘快一些。因为位运算符直接对内存数据进行操作,不需要转成十进制,因此处理速度快;

if(nums[mid]==target)
{
    return mid;
}

这句代码应该很好理解,当nums[mid]==target,就是说如果mid位置处的值恰好等于我们要找的target,那直接返回mid就可以了,后面的缩小区间再查找就不用执行了,因为数组是升序的而且元素不重复,说明只有一个位置处的值等于target

 else if(nums[mid]>target)
     {
         right=mid-1;
     }

这句就是说target在原区间的左半边,所以新区间的范围是[left,mid-1]

else
    {
         left=mid+1;
    }

 这句就是说target在原区间的右半边,所以新区间的范围是[mid+1,right]

while循环之外就是说 在数组中没有找到target,就返回-1; 

②左闭右开[left,right)

如果定义target在左闭右开的区间里,[left,right),那么边界处理问题就会发生变化,要注意这一点,否则就会出错

  • while(left<rigth),注意这里使用的是<,因为left==right在区间[left,right)是没有意义的
  • if(nums[mid]>target),right=mid,因为当前nums[mid]不等于target,去左区间继续寻找,但是这时候由于区间是左闭右开的,所以right更新为mid,

核心代码如下

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

}

这里就不给大家逐句讲解了,唯一需要注意的就是

else if (nums[mid] > target)
  {
      right = mid;
  }

这里涉及到右区间的处理问题,大家注意一下。 

接下来我们把两种形式的代码放在vs里面测试一下

#include<iostream>
#include<vector>
using namespace std;

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

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

}

int main()
{
    vector<int> vec{ 1,4,5,7,11,14,18 };
    cout << "vec: ";
    for (auto c : vec)
    {
        cout << c << " ";
    }
    cout << endl;
    int tar = 5;
   /* int tar = 2;*/
    int i=search(vec, tar);
    if (i >= 0)
        cout << tar<<" index is " << i << endl;
    else
        cout << tar<<" not found" << endl;
}

程序运行结果如下, 

 

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

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

相关文章

解密银行客户经理展业利器系列一:商机共享、创收增长

2023年银行业面临存款、贷款、利润三大变局&#xff0c;与此同时&#xff0c;商业银行的数字化转型正延展至前台建设&#xff0c;期望通过科技手段布局应对&#xff0c;数字化重装身处一线的客户经理&#xff0c;带动单位时间创收提升&#xff0c;更有力地支撑银行业务战略发展…

数字设计小思 - 谈谈复位那些事

写在前面 本系列整理数字系统设计的相关知识体系架构&#xff0c;为了方便后续自己查阅与求职准备。在FPGA和ASIC设计中&#xff0c;对于复位这个问题可以算是老生常谈了&#xff0c;但是也是最容易忽略的点。本文结合FPGA的相关示例&#xff0c;再谈一谈复位。 &#xff08;本…

带着Java基础对比学习C#基本语法

文章目录 一.引包二.构造函数三.析构函数四.C#数据类型1.值类型2.引用类型分类 五.加框(boxing)和消框(unboxing&#xff09;六.运算符七.控制语句八.类的继承九.方法参数的种类十. 操作符重载十一.this关键字十二.类的多态十三.抽象类和抽象方法十四.密封类和密封方法十五.接口…

Web3中文|乱花渐欲meme人眼,BRC-20总市值逼近10亿美元

现在的Web3加密市场&#xff0c;用“乱花渐欲meme人眼”来形容再合适不过了。 何为meme&#xff1f; “meme”这个词大概很多人都不知道如何正确发音&#xff0c;并且一看到它就会和狗狗币Dogecoin等联系在一起。那它究竟从何而来呢&#xff1f; Meme&#xff1a;[mi:m]&#x…

算法:迷宫问题

描述 定义一个二维数组 N*M &#xff0c;如 5 5 数组下所示&#xff1a; int maze[5][5] { 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, }; 它表示一个迷宫&#xff0c;其中的1表示墙壁&#xff0c;0表示可以走的路&#xff0c;只能横着走或…

JVM致命错误日志(hs_err_pid.log)分析

当jvm出现致命错误时&#xff0c;会生成一个错误文件 hs_err_pid<pid>.log&#xff0c;其中包括了导致jvm crash的重要信息&#xff0c;可以通过分析该文件定位到导致crash的根源&#xff0c;从而改善以保证系统稳定。当出现crash时&#xff0c;该文件默认会生成到工作目…

vue项目部署后提示用户有新版本

你可能在浏览器见到过上面这种UI&#xff0c;这是在vue项目重新build在服务端部署后&#xff0c;浏览器刷新页面弹出的提示&#xff0c;这时如果用户点击更新就会重载页面&#xff0c;清除之前的缓存获取最新内容。 这是怎样发生的呢&#xff1f;你可能会想到下面的方式&#x…

基于深度学习的美颜SDK技术研究报告

在美颜SDK中&#xff0c;深度学习技术的应用尤为突出。本文将从深度学习技术的角度&#xff0c;分析美颜SDK的实现原理与优化技巧。 一、在美颜SDK中的具体应用 1、人脸检测 深度学习技术可以通过训练神经网络&#xff0c;从而实现高效准确的人脸检测。 2、人脸关键点检测 …

一图看懂 dotenv 模块:从.env文件中读取键值对,并将其设置为环境变量,资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 dotenv 模块&#xff1a;从.env文件中读取键值对&#xff0c;并将其设置为环境变量&#xff0c;资料整理笔记&#xff08;大全&#xff09; 摘要模块图类关系图模块全展开【…

01-数据操作+数据预处理

1.n维数组&#xff0c;也称为张量&#xff08;tensor&#xff09;&#xff1a;tensor和ndarray没有本质区别。tensor是有数学上的严格定义&#xff0c;ndarray是计算机描述的&#xff1b;张量表示一个由数值组成的数组&#xff0c;这个数组可能有多个维度&#xff1b; 无论使用…

线程初探——生产者、消费者模式

线程初探——生产者、消费者模式 文章目录 线程初探——生产者、消费者模式生产者消费者模式结构以及问题解决问题 helllo&#xff0c;大家好&#xff0c;这里是追風者频道。今天我们来聊一聊生产者、消费者模式。后期需要采用该模式来进行协议栈抓包架构的搭建&#xff0c;通过…

电脑ppt录制微课软件哪个好 电脑ppt录制微课的方法

如今线上课程已经逐渐成为线下课程的补充&#xff0c;拓宽知识面&#xff0c;让学生能够学到更多知识。微课是线上课程里比较方便观看的一类&#xff0c;制作起来也很便捷&#xff0c;很多人会直接用ppt来制作微课&#xff0c;简单快速又能传播知识。今天就来分享一下电脑ppt录…

AUTOSAR入门

简介 AUTOSAR&#xff08;AUTomotive Open System ARchitecture&#xff09;是一种汽车软件架构标准&#xff0c;由德国大陆、博世、宝马等汽车及零部件制造商共同发起&#xff0c;拥有广泛的行业参与。其目标是为了解决汽车电子和软件系统日益复杂的问题&#xff0c;提高可重…

思科模拟器 | 访问控制列表ACL实现网段精准隔绝

文章目录 一、ACL工作原理二、ACL分类初步介绍三、标准ACL1、标准ACL的决策过程2、标通配符掩码关键字3、标准ACL网络拓扑4、标准ACL演示5、实战讲解 四、扩展ACL1、基础语法明细2、扩展ACL示例3、扩展ACL网络拓扑4、实战讲解 五、总结与提炼 一、ACL工作原理 ACL&#xff08;A…

大数据任务调度和数据同步组件初探

本文个人博客地址 背景 数据从最原始的状态&#xff0c;可能是一个 excel&#xff0c;一个文本&#xff0c;或者是来自业务数据库的数据&#xff0c;格式各种各样&#xff0c;落地到数据仓库、数据湖中&#xff0c;数据的同步过程 是必不可少的 图片来源 传统的数据同步方式…

如何解决IP能ping通但无法上网的问题?

当我们在网络环境中遇到无法上网的问题时&#xff0c;可能会尝试使用ping命令来测试网络连接是否正常。如果ping测试成功&#xff0c;说明我们的IP地址能够和网络中其他设备进行通信&#xff0c;但是无法上网。这种情况下&#xff0c;我们需要采取一些措施来解决这个问题。本文…

软考A计划-重点考点-专题三(操作系统知识)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

springboot 多模块项目

比起传统复杂的单体工程&#xff0c;使用Maven的多模块配置&#xff0c;可以帮助项目划分模块&#xff0c;鼓励重用&#xff0c;防止POM变得过于庞大&#xff0c;方便某个模块的构建&#xff0c;而不用每次都构建整个项目&#xff0c;并且使得针对某个模块的特殊控制更为方便。…

【前端知识】Cookie, Session,Token和JWT的发展及区别(三)

【前端知识】Cookie, Session,Token和JWT的发展及区别&#xff08;三&#xff09; 7. Token7.1 Token的背景及定义7.1.1 背景7.1.2 定义7.1.3 基于定义的N个思考&#xff08;1&#xff09;关于无状态的相关思考&#xff08;2&#xff09; 避免查库的思考 7.2 Token的特点&#…

记一次perf实验,检验CPU的分支预测功能

实验介绍 在上一篇文章中&#xff0c;作者通过给Alder Lake&#xff08;12th gen i5 1240p&#xff09;安装Ubuntu22.04&#xff0c;终于把PMU用起来了 $ dmesg | grep PMU [ 0.127326] Performance Events: XSAVE Architectural LBR, PEBS fmt4-baseline, AnyThread dep…