二分查找(非朴素)--在排序数组中查找元素的第一个和最后一个位置

 个人主页:Lei宝啊 

愿所有美好如期而遇


目录

本题链接 

输入描述

输出描述

算法分析

1.算法一:暴力求解

2.算法二:朴素二分算法

3.算法三:二分查找左右端点

3.1查找左端点

3.1.1细节一:循环条件

3.1.2细节二:mid的值

3.2查找右端点

解题源码 


本题链接 

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

输入描述

我们选择示例1,接口为vector<int> searchRange(vector<int>& nums, int target) ,向nums中写入非递减的数字,也就是说可以有多个重复的相同数字。

输出描述

由于nums中可能有多个值与target相等,所以我们需要记录这个区间的左端点和右端点,然后返回这个区间,如果找不到,则区间为[-1,-1]。

算法分析

1.算法一:暴力求解

直接从左到右扫描这个数组,通过begin和end变量来记录相等于target的起始位置和结束位置,找不到则返回[-1,-1],时间复杂度为O(N)。

2.算法二:朴素二分算法

直接二分,如果寻找的target在nums中只有一个,那么时间复杂度为log(N),但是如果有多个的话,我们还是需要去遍历mid的左边和mid的右边,如果整个数组所有值都与target相等,就相当于要遍历整个数组,时间复杂度仍然为O(N),这里我们使用朴素二分算法仍然不是最优的。

3.算法三:二分查找左右端点
3.1查找左端点

我们可以将数组分成两个区间,以[5,7,7,8,8,10],target = 8为例,既然我们要找左端点,也就是最左边的那个8,所以我们将数组分成小于target的区间大于等于target的区间,即[5,7,7]   [8,8,10]这两个区间。(lhs意为left side hand即左手边,rhs意为right side hand即右手边)

我们定义一个mid,mid = lhs + (rhs - lhs) / 2,并且有两种结果:

  • 当nums[mid] < target ,lhs = mid + 1;
  • 当nums[mid] >= target, rhs = mid;

你可能会问,为什么rhs = mid, 按照我们朴素二分算法的理解,应该是rhs = mid - 1, 我们可以举个例子,如果说mid = 3,也就是上面最左边的8,那么再怎么样我们也无法找到左端点了。

同时按照我们上面的两种结果,我们也可以发现一个事实,就是rhs永远不会出他的区间,lhs是可以跨区间的,当lhs == rhs时,也就代表着循环结束了。

所以这就是全部了吗?当然不是,他还有其他细节,这些细节一但注意不到,就是一个接一个的死循环。

3.1.1细节一:循环条件

朴素二分算法循环条件是lhs <= rhs,那么我们这里也是这样吗?上面我们提过一个事实,就是说当lhs == rhs时就已经结束了,而且正好是我们的左端点,所以说我们不用再去判断相等的情况,你可能会说,碰巧罢了,你这是能找到结果,你要是找不到呢?如果nums里全部大于target呢?如果nums里全部小于target呢?那我们分三种情况:

所以我们不需要判断lhs == rhs这种情况,因为我们上述三种情况就是本题的所有情况,而我们也证实了当lhs == rhs时就是我们的结果,要么找到,要么就找不到。

但是有人会犯倔,我嫌麻烦,还得想这么多东西,不就多判断一次嘛?我就用lhs <= rhs, 有问题吗?还是分三种情况,运气好点就不死循环。

所以我们循环条件也有两个细节:

  • 细节一:无需判断lhs == rhs,因为此时已经是结果了。
  • 细节二: 如果真的比较了,可能会死循环,在leetcode众多测试用例中,一定是错的。
3.1.2细节二:mid的值

我们上面直接让mid = lhs + (rhs - lhs) / 2; 可能你也没有思考为什么,可不可以,事实上,在查找左端点时这样正好可以,但是放在查找右端点上就是死循环,那么右端点mid怎么算?

mid = lhs + (rhs - lhs + 1) / 2;那么我们把这个用在左端点上看看效果。

我们可以发现:

  • 选择第一种方式,mid的值偏向于左边下标
  • 选择第二种方式,mid的值偏向于右边下标 

细节很多,不注意就死循环,但是我们不要死记硬背,要去理解,怎么样就死循环了,这才是我们该做的。

3.2查找右端点

查找右端点和查找左端点思路完全相同,只是有些细节不同,我们这里指出:

  1. 区间的划分:大于target的区间的小于等于target的区间
  2. mid的计算:mid = lhs + (rhs - lhs + 1) / 2;

剩下的就是一个模子了。 

解题源码 

class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {vector<int> v(2,-1);if(nums.size() <= 0)  return v;//计算左端点int lhs = 0, rhs = nums.size() - 1, mid = lhs + (rhs - lhs) / 2;while(lhs < rhs){//分区间if(nums[mid] < target) lhs = mid + 1;   //小于区间else  rhs = mid;                        //大于等于区间        mid = lhs + (rhs - lhs) / 2;}if(nums[rhs] == target) v[0] = rhs;//计算右端点lhs = 0, rhs = nums.size() - 1, mid = lhs + (rhs - lhs + 1) / 2;while(lhs < rhs){//分区间if(nums[mid] > target) rhs = mid - 1;   //大于区间else  lhs = mid;                        //小于等于区间        mid = lhs + (rhs - lhs + 1) / 2;}if(nums[rhs] == target) v[1] = rhs;return v;}
};

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

亚信安慧AntDB数据库引领数字时代通信创新

在数字经济与实体经济深度融合的时代&#xff0c;通信行业正迎来前所未有的新机遇。特别是在中国信通院的预测中&#xff0c;2027年5G专网市场规模预计将达到802亿元&#xff0c;呈现出显著的增长态势&#xff0c;年复合增长率高达42%。 亚信安慧AntDB数据库一直致力于紧跟科技…

K8s部署Minio使用NFS持久化存储

一、介绍 Minio是一款高性能的对象存储服务器&#xff0c;它兼容Amazon S3 API。它的设计目的是为了提供云存储服务的性能和可扩展性&#xff0c;同时还保持着本地存储的简单性和易用性。Minio可以在Linux、MacOS和Windows等操作系统上运行&#xff0c;它可以通过命令行界面或R…

车牌识别技术,如何用python识别车牌号

目录 一.前言 二.运行环境 三.代码 四.识别效果 五.参考 一.前言 车牌识别技术&#xff08;License Plate Recognition, LPR&#xff09;在交通计算机视觉&#xff08;Computer Vision, CV&#xff09;领域具有非常重要的研究意义。以下是该技术的一些扩展说明&#xff1…

FL Studio 21.2.2官方中文版重磅发布

纯正简体中文支持&#xff0c;更快捷的音频剪辑及素材管理器&#xff0c;多样主题随心换&#xff01; Mac版新增对苹果M2/1家族芯片原生支持。 丰富的主题换肤 现在大家可以通过控制色调、饱和度、亮度、文本、仪表和步进序列器的颜色来改变你的DAW外观&#xff0c; DAW“情绪…

Vue中的默认插槽详解

Vue中的默认插槽详解 在 Vue 中&#xff0c;插槽&#xff08;Slot&#xff09;是一种非常强大且灵活的机制&#xff0c;用于在组件中插入内容。Vue 提供了两种类型的插槽&#xff1a;默认插槽&#xff08;Default Slot&#xff09;和具名插槽&#xff08;Named Slot&#xff09…

页面布局--Flexbox的自动边距

标题页面布局–Flexbox的自动边距 通过简单的margin:auto&#xff0c;我们就能实现元素的多种对齐方式。 假设我们在盒子模型里有四个元素&#xff1a; 先给容器使用flex布局&#xff1a; .container {display: flex;justify-content: flex-start;align-items: center;gap: 6…

论文阅读<Contrastive Learning-based Robust Object Detection under Smoky Conditions>

论文链接&#xff1a;https://openaccess.thecvf.com/content/CVPR2022W/UG2/papers/Wu_Contrastive_Learning-Based_Robust_Object_Detection_Under_Smoky_Conditions_CVPRW_2022_paper.pdf Abstract 目标检测是指有效地找出图像中感兴趣的目标&#xff0c;然后准确地确定它们…

C++的面向对象学习(7):面向对象编程的三大特性之:继承

文章目录 前言一、继承&#xff1a;继承的类除了拥有上一级类的共性&#xff0c;也拥有自己的特性。二、继承方式&#xff1a;公有继承&#xff08;public inheritance&#xff09;、私有继承&#xff08;private inheritance&#xff09;和保护继承&#xff08;protected inhe…

代码回滚(git reset)后push失败的解决方法

问题描述 代码本地回滚之后&#xff08;即 git reset 到之前的某个历史节点&#xff09;&#xff0c;push上去失败&#xff0c;并报出以下错误信息 ! [rejected] master -> master (non-fast-forward) error: failed to push some refs to gitgithub.com:PisecesPeng/useg…

跨境电商迎来综合竞争力比拼时代 五大趋势解读跨境2024

过去几年&#xff0c;跨境电商成为外贸出口增长的一大亮点&#xff0c;随着年底国务院办公厅《关于加快内外贸一体化发展的若干措施》的发布&#xff0c;跨境电商在促进经济发展、助力内外贸一体化发展方面的价值更加凸显。 这是跨境电商变化最快的时代&#xff0c;也是跨境电…

Java核心知识点1-java和c++区别、隐式和显示类型转换

java和c区别 java通过虚拟机实现跨平台特性&#xff0c;但c依赖于特定的平台。java没有指针&#xff0c;它的引用可以理解为安全指针&#xff0c;而c和c一样具有指针。java支持自动垃圾回收&#xff0c;而c需要手动回收。java不支持多重继承&#xff0c;只能通过实现多个接口来…

音频播放软件Foobar2000 mac特点介绍

Foobar2000 mac是一款高度可定制的音频播放器&#xff0c;适用于Windows平台。它支持各种音频格式&#xff0c;包括MP3、FLAC、AAC、WMA等&#xff0c;同时也支持各种音频插件和效果器&#xff0c;可以提供更好的音质和用户体验。 Foobar2000 mac软件特点 1. 高度可定制&#…