算法训练-二分查找

news2024/11/28 2:37:14

这里写目录标题

  • 34. 在排序数组中查找元素的第一个和最后一个位置
  • 162. 寻找峰值
  • 153. 寻找旋转排序数组中的最小值
  • 33. 搜索旋转排序数组

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

在这里插入图片描述
题目链接

vector<int> searchRange(vector<int>& nums, int target) {
	     int left=0,right=nums.size()-1;
	     vector<int> ret{-1,-1};
	     while(left<=right)//区间不为空,则可以继续去找
	     {
	         int mid=(left+right)/2;
	         //int mid=left+(right-left)/2;这样写不会越界,如果left和right都很大,相加就可能越界
	         if(nums[mid]==target)
	         {
	             int i;
	             for(i=mid-1;i>=0&&nums[i]>=target;i--)
	             /*找到target不能立即存放到ret中,因为题目要求找到第一个和最后一个target位置,先从mid往前找,相同 就给ret[0]赋当前下标,对mid右变处理相同
	             因为是有序的,所以加上nums[i]>=target,往前找,不过遇到大于target则前面不可能再有了
	             */

	             if(nums[i]==target)
	             ret[0]=i;
	             if(ret[0]==-1)
	             ret[0]=mid;

	             for(i=mid+1;i<nums.size()&&nums[i]<=target;i++)
	             if(nums[i]==target)
	             ret[1]=i;
	             if(ret[1]==-1)
	             ret[1]=mid;
	         }
	         if(nums[mid]<target)
	         left=mid+1;
	         else right=mid-1;
	     }
	     return ret;

似乎这样写没有问题,并且也通过了测试用例,但是仔细分析就会发现,在某些特定数组的情况
比如:target=2 序列是:1 22222222222222222222222222222222…2222 1
中间有很多很多2,那么这个算法的效率就不是O(logn)了,接近O(n)

正确写法:

     那么问题出在哪里,因为我们的二分是对小于(mid<target)进行mid+1,大于进行mid-1,相等就往前找,和往后找,这里简单理解为return
     而应该在相等时也不return,相等应该归类于大于的情况,说明前面还可能有,继续找
     这时就可以理解为right指针不断向前,并且是跳跃式向前找,target,最后left的位置就是第一个target的位置,
     为什么是left而不是right,因为right不断向前最中总能找到一个不等的或者区间为空了

     1.不等情况:此时right指向的不等于target,那么肯定是mid小于了,left指针此时向后移动,如此循环直到区间只剩一个元素,则该元素就是第一个target
     2.区间为空:则不存在这样的元素,那么此时left和right的情况是怎样的?
     区间为空说明mid一次都没有匹配target,则left一直后移,最终left指向right+1,全过程right都没有变化
     区间为空还有一种情况是数组中没有target,但是两遍是符合的,也就是left和right都会移动
    inline int lower_bound(vector<int>& nums,int target)
    {
        int left=0,right=nums.size()-1;
        while(left<=right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]<target)
            left=mid+1;
            else 
            right=mid-1;//为什么没有相等,以及下面为什么返回left,都在上面解释过了
        }
        return left;
    }
    vector<int> searchRange(vector<int>& nums, int target) {
        int  start=lower_bound(nums,target);
        if(start==nums.size()||nums[start]!=target)//区间为空有两种情况
        return {-1,-1};
        int end=lower_bound(nums,target+1)-1;//end要找target+1,最后在-1找前一个
        return {start,end};
    }
};

返回值为vector<int>为什么可以返回return {start,end};
因为 vector<int> arr{ 1, 1};可以用初始化列表初始化容器vector

感觉第二种不好理解啊,low_bound还行,但是在searchRange中比较复杂,还是第一种的思路简单,有没有什么办法优化第一种呢?

方法一的问题在于,遇到特殊相等之后就不在是二分算法了,那么应该当相等时,继续二分,更新边界,那边界应该如何去选择呢,这就要看是要找第一个target还是第二个target,对于ret[0],则要更新right=mid+1
对于ret[1],更新left=mid-1,分两次二分就可以解决

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        return {searchLeftOrRightBound(nums, target, "left"), searchLeftOrRightBound(nums, target, "right")};
    }
private:
    int searchLeftOrRightBound(vector<int>& nums, int target, const string& bound) {
        int left = 0, right = nums.size() - 1;
        int res = -1;
        while (left <= right) {
            int mid = (left + right) >> 1;
            if (nums[mid] < target) {
                left = mid + 1;
            }
            else if (nums[mid] > target) {
                right = mid - 1;
            }
            else {
                res = mid;
                if (bound == "left") {
                    right = mid - 1;
                }
                else if (bound == "right") {
                    left = mid + 1;
                }
            }
        }
        return res;
    }
};

162. 寻找峰值

在这里插入图片描述
题目链接

class Solution {
public:
/*这道题,最最最重要的是条件,条件,条件,两边都是负无穷,数组当中可能有很多波峰,也可能只有一个,如果尝试画图,就跟股票信息一样,没有规律,如果根据中点値判断我们的二分方向该往何处取, 这道题还有只是返回一个波峰。你这样想,中点所在地方,可能是某座山的山峰,山的下坡处,山的上坡处,如果是山峰,最后会二分终止也会找到,关键是我们的二分方向,并不知道山峰在我们左边还是右边,送你两个字你就明白了,爬山(没错,就是带你去爬山),如果你往下坡方向走,也许可能遇到新的山峰,但是也许是一个一直下降的坡,最后到边界。但是如果你往上坡方向走,就算最后一直上的边界,由于最边界是负无穷,所以就一定能找到山峰,总的一句话,往递增的方向上,二分,一定能找到,往递减的方向只是可能找到,也许没有。*/
//二分的过程就是模拟爬山,寻找波峰
    int findPeakElement(vector<int>& nums) {
        int left=0,right=nums.size()-1;

        while(left<right)
        {
            int mid=(left+right)/2;
            if(nums[mid]<nums[mid+1])//左侧小于右侧,说明是在爬坡,并且题目说nums[n] = -oo
            left=mid+1;
            else right=mid;//不能是mid-1,因为mid也有可能是个波峰
        }
        return left;
    }
};

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

在这里插入图片描述
题目链接

class Solution {
    public:
    /*
    根据题意可以知道:该序列有两段上坡,可以视为一段高坡,一段低坡
    并且对于末尾元素x,最小值右侧都大于x,最小值左侧都小于x
    */
    int findMin(vector<int> & nums) {
        int left = 0, right = nums.size() - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < nums[nums.size() - 1]) //和末尾元素比较。mid小于末尾,说明在低坡。最小值一定在mid左侧,并且mid的位置有可能就是最小值位置,所以是right=mid(这是因为最小值一定在小坡上,小坡坡底)
                right = mid;
            else //mid 大于末尾,说明在一定在大坡,大坡是不可能有最小值的,直接mid+1
                left = mid + 1;
        }
        return nums[left];
    }
};

33. 搜索旋转排序数组

在这里插入图片描述
题目链接

思路

先找到要二分的区间位置,有两个坡,那就先找最小值位置,通过比较target和最小值,来判断在哪个坡

class Solution {
    public:
    int search(vector<int> & nums, int target) {

        int left = 0, right = nums.size() - 1;
        bool asc = true;//升序标记

        if (nums[left] > nums[right])
            asc = false;

        if (asc == false) {//逆序,说明进行了旋转,有两个坡,先找坡底最小值
            if (target < nums[left] && target > nums[right])
                return -1;
            while (left < right) {
                int mid = left + (right - left) / 2;
                if (nums[mid] < nums[nums.size() - 1])
                    right = mid;
                else left = mid + 1;
            }
            if (target < nums[0])
                right = nums.size() - 1;
            else left = 0;
            cout << "left=" << left << " " << right << endl;
        }
        //确定好在哪个坡了,标准二分
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] == target)
                return mid;
            else if (nums[mid] < target)
                left = mid + 1;
            else right = mid - 1;
        }
        return -1;
    }
};

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

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

相关文章

集合 集合

目录 ArraryList 引用基本类型 案例1&#xff1a;定义一个集合添加学生姓名年龄 案例2&#xff1a;查看是否存在这个id 案例3&#xff1a;手机 案例4&#xff1a;学生管理系统&#xff08;不完整&#xff09; Collection collection遍历方式 迭代遍历&#xff08;不依赖…

深度学习用于医学预后-第二课第四周13-15节-使用生存数的非线性风险评估模型

使用生存数的非线性风险评估模型 在这堂课中&#xff0c;你将学习关于生存树的知识。你将学习如何将患者的变量纳入考虑&#xff0c;比较不同患者的风险。 你将看到生存树与二叉决策树相似&#xff0c;可以让你构建模型来捕捉患者数据中的非线性关系。 在这堂课中&#xff0…

分布式运用之rsync远程同步

1.rsync的相关知识 1.1 rsync简介 rsync&#xff08;Remote Sync&#xff0c;远程同步&#xff09;是一个开源的快速备份工具&#xff0c;可以在不同主机之间镜像同步整个目录树&#xff0c;支持增量备份&#xff0c;并保持链接和权限&#xff0c;且采用优化的同步算法&#…

怎么使用树莓派总要了解一点它的软硬件吧。什么模块有什么用,需要什么准备才能安全的看到树莓派的开机界面

Raspberry Pi(中文名为“树莓派”,简写为 RPi&#xff0c;(或者 RasPi / RPI)只有信用卡大小的微型电脑&#xff0c;其系统基于 Linux。随着 Windows 10 IoT 的发布&#xff0c;我们也将可以用上运行 Windows IoT 的树莓派。别看其外表“娇小”&#xff0c;内“心”却很强大&am…

Doxygen 源码分析: SymbolMap类

2023-05-21 10:59:35 ChrisZZ imzhuofoxmailcom Hompage https://github.com/zchrissirhcz 文章目录 1. Doxygen 版本2. SymbolMap 类概要3. 添加符号: SymbolMap<T>::add()4. 删除符号: SymbolMap<T>::remove()5. 符号查找: SymbolMap<T>::find()6. 哪里用了…

1.golang的介绍、环境安装和编译器安装

一、Go的介绍 Go语言其实是Golanguage的简称&#xff0c;Go&#xff08;又称 Golang&#xff09;是 Google 的 Robert Griesemer&#xff0c;Rob Pike 及 Ken Thompson 开发的一种静态强类型、编译并发型语言。Go 语言语法与 C 相近&#xff0c;但功能上有&#xff1a;内存安全…

Doxygen 源码分析: Definition类

2023-05-21 13:05:28 ChrisZZ imzhuofoxmailcom Hompage https://github.com/zchrissirhcz Blog https://blog.csdn.net/baiyu33 文章目录 1. Doxygen 版本2. Definition 类和它的8个子类3. Definition 类的 Private 成员4. Definition 类的 Public 成员4.1 特殊成员函数4.2 获…

(学习日记)AD学习 #1

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

Linux基本操作指令2

目录 指令1&#xff1a;rmdir指令&#xff1a; 指令2&#xff1a;rm命令&#xff1a; 指令3&#xff1a;man指令&#xff1a; 指令4&#xff1a;cp指令&#xff1a; 指令5&#xff1a;mv指令&#xff1a;类似于Windows的剪贴 指令6&#xff1a;cat指令 指令7&#xff1a;…

QT5.15.0使用gcc-arm-8.2-2018.08-x86_64-arm-linux-gnueabihf交叉编译的问题总结

目录 一、交叉编译 二、操作中踩过的坑 1、环境变量未生效 2、交叉编译QT代码操作 3、烧录时报错缺少xcb问题 4、小白的细小错误 三、--platform命令 3、1 -platform linuxfb 详细文档请点击此处 我的文档在原文档的基础上添加了非常详细的提醒&#xff0c;可以少走弯路…

软件性能测试过程详解与案例剖析

软件性能测试是软件开发过程中至关重要的一环&#xff0c;它能够帮助我们确保软件在不同负载和使用情况下的表现。在软件性能测试中&#xff0c;我们通常会关注软件的响应时间、吞吐量、并发用户数等指标&#xff0c;以评估软件性能。 软件性能测试过程主要分为以下几个步骤&am…

leetcode:322. 零钱兑换(暴力dfs,记忆化dfs,动态规划(朴素+优化),bfs+贪心)

记录常规的完全背包问题模型 1.暴力dfs2.优化dfs&#xff0c;记忆化dfs3.动态规划4.bfs 1.由于每件物品可以无限取&#xff0c;那么可以发现这是一个完全背包问题模型。 1.暴力dfs 最后要求的是&#xff1a;n种硬币&#xff0c;凑成总金额为amount。每种硬币无限取&#xff0…

Java8 教你一行代码搞定:如何计算map中value值

大家好&#xff0c;我是三叔&#xff0c;很高兴这期又和大家见面了&#xff0c;一个奋斗在互联网的打工人。 这期给大家讲一下在Java编程中&#xff0c;如何使用Java8对map的值进行计算&#xff0c;在实际开发中&#xff0c;也是经常遇到统计map中的value值之和。 Map是一种常…

Web安全:文件上传漏洞测试.

Web安全&#xff1a;文件上传漏洞测试. 现在大多的网站和Web应用系统都会有上传功能&#xff08;比如&#xff1a;文档&#xff0c;图片&#xff0c;头像&#xff0c;视频上传等.&#xff09;&#xff0c;而程序员在开发文件上传功能时&#xff0c;没有对代码做严格校验上传文…

解决大文件传输难题的方法和技巧

传统的传输大文件的方式&#xff0c;如电子邮件附件或USB驱动器&#xff0c;由于文件大小的限制和安全问题&#xff0c;变得越来越不方便。大文件共享是现代商业通信的一个重要方面&#xff0c;组织需要安全可靠的方式来传输这些文件。 传统文件传输方式的不便 传统的文件传输方…

LabVIEWCompactRIO 开发指南27 创建模块化、可重复使用的子VI

LabVIEWCompactRIO 开发指南27 创建模块化、可重复使用的子VI 编写模块化代码几乎总是一个好主意&#xff0c;无论是为Windows、实时还是FPGA设备设计应用程序。子VI使代码更易于调试和故障排除&#xff0c;更易于记录和跟踪更改&#xff0c;并且通常更清晰&#xff0c;更易于…

一文读懂JVM架构解析

JVM 架构解析 Java 架构JVMJVM是如何工作的&#xff1f;类加载器子系统 运行时数据区执行引擎 每个 Java 开发人员都知道字节码经由 JRE&#xff08;Java运行时环境&#xff09;执行。但他们或许不知道 JRE 其实是由 Java虚拟机&#xff08;JVM&#xff09;实现&#xff0c;JVM…

css3 flex弹性布局学习

一、flex基本概念 当开启flex布局后&#xff0c;项目默认沿主轴排列。单个项目占据的主轴空间叫做main size&#xff0c;占据的交叉轴空间叫做cross size。 二、容器的属性 以下6个属性设置在容器上。 flex-direction flex-wrap flex-flow justify-content align-items align…

LabVIEWCompactRIO 开发指南26 同步循环

LabVIEWCompactRIO 开发指南26 同步循环 对于大多数控制和监视应用&#xff0c;代码执行的时间对于系统的性能和可靠性非常重要。在此电机控制示例中&#xff0c;有两个不同的时钟信号&#xff1a;采样时钟和PID时钟。这些是在应用程序中生成的布尔信号&#xff0c;用于在循环…

【HackTheBox MonitorsTwo】打靶记录

信息搜集 1、nmap 扫描一波 └─# nmap -sC -sV 10.10.11.211 Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-14 20:55 EDT Nmap scan report for 10.10.11.211 Host is up (0.25s latency). Not shown: 998 closed tcp ports (reset) PORT STATE SERVICE VERSION 2…