二分查找常用解题模板(带一道leetcode题目)

news2024/9/8 22:59:29

1.为了较为清晰的写出各种情况,接下来的代码中不会出现else,而是将每一个else if均给写出来!!!
2.为了防止每次的mid溢出,我们均写为mid = left + (right - left)

基本的二分查找模板(寻找一个数)

基本问题描述:在一组数中能否找到一个目标数target,若找到则返回其索引,找不到则返回-1;

代码:

/**
     * Basic binary search template
     *
     * @param nums   Given array
     * @param target Given target number
     * @return int
     */
    int binarySearch(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right - left);
            //Have found
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] < target) {
                left = mid + 1;
            }
        }
        return -1;
    }

具体细节分析可参考另一篇文章:

二分查找中的部分细节与模版1(附带力扣题目)

寻找左、右侧边界的二分查找模板

新问题的提出

现在给你一个数组nums = [[1,2,2,2,3];和一个目标值target = 2,假如现在要求还是使用二分查找对数级的时间复杂度来找出target左、右则边界;该如何实现?

由上述问题我们可以引出寻求左、右边界二分查找模板

寻找左侧边界的二分查找模板

/**
     * Find a set of numbers that are smaller than target
     *
     * @param nums   Given array
     * @param target Given target number
     * @return int
     */
    int left_bound(int[] nums, int target) {
        if (nums.length == 0) {
            return -1;
        }
        int left = 0;
        int right = nums.length;//Look out
        while (left < right) { //Look out
            int mid = left + (right - left);
            //Have found
            if (nums[mid] == target) {
                right = mid;
            } else if (nums[mid] > target) {
                right = mid;//Look out
            } else if (nums[mid] < target) {
                left = mid + 1;
            }
        }
        return left;
    }
寻找左侧边界模板的细节解释

1.为什么while中此时是 **<**而不是 <=;

因为此时right = nums.length,则表示的搜索区间是**[left, right)左闭右开**;则while (left < right) 的终止条件是left == right,此时的搜索区间**[left, right)**为空,即可以正确终止。

2.如果nums中不存在target该返回什么?(代码中没有返回**-1**的操作)

请读者先按上述代码手动模拟给定nums为[1,2,2,2,3]、[2,3,5,7]、[2,3,5,7],target分别对应为2,1,8时代码会返回什么(答案分别是:1,0,4);
对此我们对这段代码的理解是对于给定数组nums中存在多少个数小于target!!!
综上函数返回值left的取值范围为[0, left.length];所以添加两行代码就能在正确的时候return -1;

//target is larger than all the numbers
if (taeget == nums.length) {
	return -1;
}
return nums[left] == target ? left : -1;

3.为什么是left = mid + 1; right = mid;

由于此时我们的搜索区间为**[left, right)左闭右开**,所以当nums[mid]被检测完后下一步就是去掉mid分割为两个区间,即**[left mid) [mid + 1, right) 两个区间也均是左闭右开的**

4.为什么该算法能够搜索左侧边界?

关键在于**nums[mid] == target时right = mid;**即找到target时不是立即返回,而是缩小搜索区间,达到锁定左侧边界的目的。

5.为什么返回left而不是right?

因为此时循环退出条件时left == right,所以返回left还是right都是一样的!!!

寻找左边界模板的另一种写法(与基本模板对齐)

为了使得改模板在形式上尽量和基本模板对其(方便我们的记忆),我们先要做如下改写:

1.right = nums.length - 1;此时搜索区间就是全闭
2.由于此时搜索区间是全闭的所以循环的终止条件就是left == right + 1;所以此时while循环的退出条件是left <= right;
3.由于此时搜索区间是全闭的,所以在去除mid后余下的两个区间也应该是全闭的则**[left, mid - 1] 、[mid + 1, right]**

则此时我们可以初步写出大致的代码:

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

但是此时我们注意到由于while退出条件是left == right + 1,所以当target比nums中的所有元素都大时,则会存在索引越界的情款!!!
在这里插入图片描述
则要添加上一段检测索引越界情况的代码:

if (left >= nums.length || nums[left] != target) {
	return -1;
}
return left;

则最终的完整代码模板是:

/**
     * Look for the left boundary template
     *
     * @param nums   Given array
     * @param target Given target number
     * @return int
     */
    int left_bound(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] == target) {
                right = mid - 1;
            }
        }
        if (left >= nums.length || nums[left] != target) {
            return -1;
        }
        return left;
    }
}

寻找右侧边界二分查找模板

左闭右开写法:

/**
     * Look for the right boundary template
     *
     * @param nums   Given array
     * @param target Given target number
     * @return int
     */
    int right_bound(int[] nums, int target) {
        int left = 0;
        int right = nums.length;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid;
            } else if (nums[mid] == target) {
                right = mid + 1;
            }
        }
        return left - 1;
    }
寻找右侧边界二分查找模板的细节解释

1.为什么可以找到右侧边界

关键在于if(nums[mid] == target) left = mid + 1;是不断在增大搜索区间的下界left使得区间不断向右收缩,达到锁定右侧边界的目的.

2.为什么最终返回left-1?

因为我们对 left 的更新必须是 left = mid + 1 ,就是说 while 循环结束时, nums[left] ⼀定不等于 target 了,⽽ nums[left-1] 可能是target 。

3.为什么没有返回 -1 的操作?如果 nums 中不存在 target 这个值,怎么办?

类似之前的左侧边界搜索,因为 while 的终⽌条件是 left == right ,就是说 left 的取值范围是 [0, nums.length] ,所以可以添加两⾏代码,正确地返回 -1:

while (left < right) {

}
if (left == 0) {
	return -1;
}
return nums[left - 1] == target ? (left - 1) : -1;
寻找右侧边界二分查找模板的另一种写法(对齐基本二分查找模式)
/**
     * Look for the right boundary template
     *
     * @param nums   Given array
     * @param target Given target number
     * @return int
     */
    int right_bound(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] == target) {
                //Contracted left boundary
                left = mid + 1;
            }
        }
        // Instead, check the right out of bounds.
        if (right < 0 || nums[right] != target) {
            return -1;
        }
        return right;
    }

当target比所有元素小时,right会被减到-1,所以最后要防止越界.
在这里插入图片描述

力扣题目(35. 搜索插入位置)

题目描述:

在这里插入图片描述
在这里插入图片描述

解题思路:

由于当target大于nums中的所有数时,直接放置于nums[nums.length - 1];当target小于nums中的所有数时,直接放置于nums[0];其它情况时则等价求取给定数组中小于给定值target的个数有多少个,则直接套用前面寻找左侧边界模板

代码:

class Solution {
public:
    /**
     * Binary search
     * 
     * @param nums Given array
     * @param target Given number
     * @return int
     */
    int searchInsert(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) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] == target) {
                right = mid - 1;
            }
        }
        return left;
    }
};

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

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

相关文章

计算机网络物理层知识点总结

本篇博客是基于谢希仁编写的《计算机网络》和王道考研视频总结出来的知识点&#xff0c;本篇总结的主要知识点是第二章的物理层。上一章的传送门&#xff1a;计算机网络体系结构-CSDN博客 通信基础 物理层概念 物理层解决如何在连接各种计算机的传输媒体上传输数据比特流&am…

服务器上部署WEb服务方法

部署Web服务在服务器上是一个比较复杂的过程。这不仅仅涉及到配置环境、选择软件和设置端口&#xff0c;更有众多其它因素需要考虑。以下是在服务器上部署WEb服务的步骤&#xff1a; 1. 选择服务器&#xff1a;根据项目规模和预期访问量&#xff0c;选择合适的服务器类型和配置…

MySQL:函数

提醒&#xff1a; 设定下面的语句是在数据库名为 db_book里执行的。 创建user_info表 注意&#xff1a;pwd为密码字段&#xff0c;这里使用了VARCHAR(128)类型&#xff0c;为了后面方便对比&#xff0c;开发项目里一般使用char(32)&#xff0c;SQL语句里只有MD5加密函数 USE db…

iOS卡顿原因与优化

iOS卡顿原因与优化 1. 卡顿简介 卡顿&#xff1a; 指用户在使用过程中出现了一段时间的阻塞&#xff0c;使得用户在这一段时间内无法进行操作&#xff0c;屏幕上的内容也没有任何的变化。 卡顿作为App的重要性能指标&#xff0c;不仅影响着用户体验&#xff0c;更关系到用户留…

XUbuntu22.04之解决:仓库xxx没有数字签名问题(二百一十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

buuctf_misc_荷兰宽带数据泄露+被偷走的文件

荷兰宽带数据泄露 题目&#xff1a; 没啥&#xff0c;工具给大家放这了&#xff0c;这个&#xff08;相对来说&#xff09;比较安全 https://routerpassview.en.lo4d.com/windows 打开后&#xff0c;.bin文件直接托进去 只是我想不到的是&#xff0c;flag这算是username&…

H.266参考软件VTM各版本的性能差异

VTM&#xff08;VVC Test Model&#xff09;&#xff0c;是H.266视频编码标准的参考软件&#xff0c;即是VVC spec.的一种参考实现&#xff0c;代码里包括了H.266的软件编码器和软件解码器实现&#xff0c;代码地址如下&#xff1a; https://vcgit.hhi.fraunhofer.de/jvet/VVCS…

基于单片机的节能窗控制系统设计

摘 要:本文以单片机为基础,对节能窗控制系统进行了科学设计,在满足日常生活需求的同时更好地实现节能减排目标。此设计中的节能窗控制系统,实际操作要灵活,具备可靠且稳定的性能,同时具备节能功效。 关键词:单片机;节能窗控制系统;系统设计 在节能窗等概念推广的背景…

前端学习第二天-html提升

达标要求 了解列表的分类 熟练掌握列表的用法 熟练掌握表格的结构构成 合并单元格 表单的组成 熟练掌握表单控件分类的使用 1.列表 1.1 无序列表 <ul>&#xff1a;定义无序列表&#xff0c;并且只能包含<li>子元素。 <li>&#xff1a;定义列表项&a…

hippy 调试demo运行联调-mac环境准备篇

适用对于终端编译环境不熟悉的人看&#xff0c;仅mac端 hippy 调试文档官网地址 前提&#xff1a;请使用node16 联调预览效果图&#xff1a; 编译iOS Demo环境准备 未跑通&#xff0c;待补充 编译Android Demo环境准备 1、正常安装Android Studio 2、下载Android NDK&a…

10-Java装饰器模式 ( Decorator Pattern )

Java装饰器模式 摘要实现范例 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其结构 装饰器模式创建了一个装饰类&#xff0c;用来包装原有的类&#xff0c;并在保持类方法签名完整性的前提下&#xff0c;提供…

JProfiler 14 for Mac 14.0激活版:Java性能分析的终极工具

JProfiler是一款专业的Java应用程序性能分析工具&#xff0c;可帮助开发人员识别和解决Java应用程序中的性能问题。JProfiler支持Java SE、Java EE和Android平台&#xff0c;提供了多种分析选项&#xff0c;包括CPU分析、内存分析和线程分析等。 软件下载&#xff1a;JProfiler…

本地快速部署谷歌开放模型Gemma教程(基于WasmEdge)

本地快速部署谷歌开放模型Gemma教程&#xff08;基于WasmEdge&#xff09; 一、介绍 Gemma二、部署 Gemma2.1 部署工具2.1 部署步骤 三、构建超轻量级 AI 代理四、总结 一、介绍 Gemma Gemma是一系列轻量级、最先进的开放式模型&#xff0c;采用与创建Gemini模型相同的研究和技…

利用IP地址识别风险用户:保护网络安全的重要手段

随着互联网的发展和普及&#xff0c;网络安全问题日益突出&#xff0c;各种网络诈骗、恶意攻击等风险不断涌现&#xff0c;给个人和企业的财产安全和信息安全带来了严重威胁。在这样的背景下&#xff0c;利用IP地址识别风险用户成为了保护网络安全的重要手段之一。IP数据云探讨…

太阳能供电井盖-物联网智能井盖监测系统-旭华智能

在这个日新月异的科技时代&#xff0c;城市的每一个角落都在悄然发生变化。而在这场城市升级的浪潮中&#xff0c;智能井盖以其前瞻性的科技应用和卓越的安全性能&#xff0c;正悄然崭露头角&#xff0c;变身马路上的智能“眼睛”&#xff0c;守护城市安全。 传统的井盖监测系统…

Facebook直播网络需要满足什么条件

Facebook直播已经成为了企业、个人和组织开展在线活动、互动和营销的重要平台之一。然而&#xff0c;要确保Facebook直播的顺利进行和观众体验的良好&#xff0c;需要满足一系列关键条件。本文将探讨Facebook直播网络 需要满足的关键条件。 1、稳定的互联网连接&#xff1a; 稳…

【airtest】自动化入门教程(二)airtest操作

目录 一、touch 二、wait 三、swipe 四、exists 五、text 六、keyevent 七、snapshot 八、sleep 九、断言 9.1 assert_exists 9.2 assert_not_exists 9.3 assert_equal 9.4 assert_not_equal 前言&#xff1a;本文主要针对aritest部分的基础操作,aritest是一个跨平…

加密与安全_探索口令加密算法(PBE)

文章目录 概述疑问PBE 算法 &#xff08; Password Based Encryption&#xff09;CodePOM实现 小结 概述 加密与安全_探索对称加密算法中我们提到AES加密密钥长度是固定的128/192/256位&#xff0c;而不是我们用WinZip/WinRAR那样&#xff0c;随便输入几位都可以。 这是因为对…

Mac 以SH脚本安装Arthas

SH脚本安装Aethas curl -L https://alibaba.github.io/arthas/install.sh | sh安装脚本说明 示例源文件&#xff1a; #! /bin/bash# temp file of as.sh TEMP_ARTHAS_FILE"./as.sh.$$"# target file of as.sh TARGET_ARTHAS_FILE"./as.sh"# update timeo…

alpine创建lnmp环境alpine安装nginx+php5.6+mysql

前言 制作lnmp环境&#xff0c;你可以在alpine基础镜像中安装相关的服务&#xff0c;也可以直接使用Dockerfile创建自己需要的环境镜像。 注意&#xff1a;提前确认自己的alpine版本&#xff0c;本次创建基于alpine3.6进行创建&#xff0c;官方在一些版本中删除了php5 1、拉取…