当前位置: 首页 > article >正文

【笑着写算法系列】二分查找

目录

前言

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

二、搜索插入位置

三、山脉数组的封顶索引

四、寻找峰值

五、寻找旋转排序数组中的最小值


前言

这篇文章已然来到二分查找的题目,二分查找相信大家很多都听过,学校也常教,但是二分远不止学校教的那种形式简单,二分代码看着简单,但对于有些题目思想还是复杂,且细节多很容易发生死循环。

了解二分的两套使用模板:

求最左端点:

 int left=0,right=nums.length-1;
 int mid;

while(left<right){
         mid=(right-left)/2+left;
        if(nums[mid]<target) left=mid+1;
        else right=mid;
  }

如果我们将target值设为6那么,那么left和right最后的指的下标是最靠左的6

求最右端点:

int left=0,right=nums.length-1;
int mid;
while(left<right){
         mid=(right-left+1)/2+left;
         if(nums[mid]<=target) left=mid;
         else right=mid-1;
 }

这里left和right指的会是最右边的最后一个6的下标。

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

题目解析:在一个非递减的数组中,找出最左边的target和最右边的tagret下标。也就是说target有可能是有多个的。

参考代码及其解析:

我们先创建好要返回的数组,储存上两个-1。

然后开始进行二分查找(最左端二分查找)。

最左端得二分查找中左后在left==right时结束循环,如果存在结果则这个结果一定是最左边的。

而且在其中有3个小细节:

  1. mid=(right-left)/2+left;在求最左端的target时,mid的求值必须是这个。
  2. 结束条件必须是left<right,不能有等号。
  3. 在没结束前的循环中[lefft,mid]这段子数组中,永远都没有target。所以left总是要跳出这段区间left=mid+1而我们的right=mid,因为他不能跳出[mid,right]的区间中,结果可能存在这段区间中,如果没有则代表整段数组中都没有target。

循环结束后我们直接判断target即可,如果相等代表找到最左端的target,后面就直接往后找最右端即可

最右端的查找方式与左端相似,不同的只要是right=mid-1;left=mid;以及mid=(right-left+1)/2+left;

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] arr={-1,-1};

        if(nums.length==0) return arr;


        int left=0,right=nums.length-1;
        int mid;

        while(left<right){
            mid=(right-left)/2+left;
            if(nums[mid]<target) left=mid+1;
            else right=mid;
        }



        if(nums[left]==target){
            arr[0]=left;
            arr[1]=right;
        }else return arr;

        left=0;right=nums.length-1;

        while(left<right){
            mid=(right-left+1)/2+left;
            if(nums[mid]<=target) left=mid;
            else right=mid-1;
        }

        if(nums[right]==target){
            arr[1]=right;
        }



        return arr;

    }
}

注意数组里没有元素的情况。

二、搜索插入位置

题目解析:很简单就是在一个升序数组中找到一个位置下标插入target后还是一个升序数组。

代码及其解析:这里我们直接使用求最左端点的二分查找即可,注意的是如果数组中全都比target小,我们此时需要做特殊处理因为这样我们查到的下标是数组最后一个,但我们需要返回的是下一位。如:1,2,3,4.target=5

此时我们需要插入下标4的位置,但我们的二分是查不到那里的。

class Solution {
    public int searchInsert(int[] nums, int target) {
        if(nums[0]>target) return 0;
        if(nums[nums.length-1]<target) return nums.length;


        int left=0,right=nums.length-1,mid;
        while(left<right){           
            mid=(right-left)/2+left;
            if(nums[mid]<target) left=mid+1;
            else right=mid;
        }


        return right;
    }
}

三、山脉数组的封顶索引

题目解析:就是在一个峰值数组中找到那个最大的值的下标。

参考答案及其解析:

这道题这么一看好像不太符合二分的使用条件啊,毕竟在我们平时认识得二分都是要说在递增或递减的情况下使用二分算法,但其实二分的使用条件是有二段性即可。即left和right所在的那段区间的值都有不同的性质。

这道题我们使用一个普通的求右端点的代码,直接返回结果即可。

class Solution {
    public int searchInsert(int[] nums, int target) {
     
        if(nums[nums.length-1]<target) return nums.length;


        int left=0,right=nums.length-1,mid;
        while(left<right){           
            mid=(right-left)/2+left;
            if(nums[mid]<target) left=mid+1;
            else right=mid;
        }


        return right;
    }
}

四、寻找峰值

题目解析:在一个数组中,这个数组中元素的大小可能是一条波浪线的即存在多个峰值,那么这时我们返回其中一个即可。

代码及其解析:

首先我们判断这个数组是否有二段性呢,其实也是有的,left所在的位置永远都可以在递增区间,right永远在递减区间,这就是二段性

那么还是套用一个简单的二分模板即可

class Solution1 {
    public int findPeakElement(int[] nums) {
        int left=0,right=nums.length-1,mid;
        while(left<right){
            mid=(right-left+1)/2+left;
            if(nums[mid-1]<=nums[mid]) left=mid;
            else right=mid-1;
        }
        return right;
    }
}

五、寻找旋转排序数组中的最小值

代码及其解析:

首先我们看题目我们要返回的是翻转的数组中最小的那个值,那么我们可以看到例子中,在分割的两个数,5,1以及第二题的7,0无论哪一题都是返回第二个值,那么我们这时就可以使用二分,且用求最左端点的方式更加简便,及如下代码:

class Solution {
    public int findMin(int[] nums) {
        
         int left=0,right=nums.length-1,mid;
        while(left<right){
            mid=(right-left)/2+left;
            if(nums[mid]>nums[right]) left=mid+1;
            else right=mid;
        }

        return nums[left];
    }
}


http://www.kler.cn/a/526443.html

相关文章:

  • C++ 堆栈分配的区别
  • docker配置mysql并使用mysql connector cpp编程
  • 【漫话机器学习系列】067.希腊字母(greek letters)-写法、名称、读法和常见用途
  • java——继承
  • AI大模型开发原理篇-2:语言模型雏形之词袋模型
  • 【教学类-89-01】20250127新年篇01—— 蛇年红包(WORD模版)
  • 在 WSL2 中重启 Ubuntu 实例
  • 特殊Token区域与共享区域
  • 分享|借鉴传统操作系统中分层内存系统的理念(虚拟上下文管理技术)提升LLMs在长上下文中的表现
  • LINUX部署微服务项目步骤
  • C++:多继承习题5
  • 文件(c语言文件流)
  • AI时序预测: iTransformer算法代码深度解析
  • UE学习日志#15 C++笔记#1 基础复习
  • 无线通信与人工智能技术与发展年度总结
  • MYSQL 商城系统设计 商品数据表的设计 商品 商品类别 商品选项卡 多表查询
  • Kafka 压缩算法详细介绍
  • 【股票数据API接口41】如何获取股票指最新分时MA数据之Python、Java等多种主流语言实例代码演示通过股票数据接口获取数据
  • gesp(C++六级)(7)洛谷:P10376:[GESP202403 六级] 游戏
  • 范冰冰担任第75届柏林电影节主竞赛单元评委 共鉴电影佳作
  • CF1098F Ж-function
  • F. Ira and Flamenco
  • 智慧园区系统助力企业智能化升级实现管理效率与安全性全方位提升
  • B站吴恩达机器学习笔记
  • C++11之列表初始化
  • 不够专业,想更体系化