二分法的总结

2024-06-12 18:36
文章标签 总结 二分法

本文主要是介绍二分法的总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、前言

最初始版的二分法是力扣704. Binary Search,而后面的二分法都是在这个基础上进行的变化

class Solution {
public:int search(vector<int>& nums, int target) {int left=0;int right=nums.size()-1;while(left<=right){//在这里选择的是左闭右闭的区间,左闭右开也是可以的int middle=(left+right)/2;//这里其实最好还是写成(right-left)/2+left,防止溢出if(nums[middle]==target){return middle;}else if(nums[middle]>=target){right=middle-1;//right是否能够等于middle-1还是要看自己写的区间是左闭右闭还是左闭右开}else{left=middle+1;}}return -1;}
};

其次,我开始刷了第二道求下标的题目力扣35. Search Insert Position,这道题我一开始想不懂为什么突然多了一个ret,为什么要用ret来记录下标,不能直接返回吗?什么时候要用ret来表示,什么时候又不用呢?这些问题在后面解答。

class Solution {
public:int searchInsert(vector<int>& nums, int target) {int left=0;int right=nums.size()-1;int ret=nums.size();while(left<=right){//这里依旧选择了左闭右闭的区间int middle=left+(right-left)/2;if(nums[middle]>target){ret=middle;right=middle-1;}else if(nums[middle]<target){left=middle+1;}else{return middle;}}return ret;}
};

再接下来就是力扣69. Sqrt(x)。这道题我就开始更蒙圈了,和704比起来,不仅要用ret来记录,而且if条件也变了,虽然我当时想到了要这样写,但是if else判断句直接给我干晕了,为什么这里只用两个判断句就行了呢?

class Solution {
public:int mySqrt(int x) {int left=0;int right=x;int ret;while(left<=right){//依旧使用左闭右闭区间int middle=left+(right-left)/2;if((long long)middle*middle<=x){ret=middle;left=middle+1;}else{right=middle-1;}}return ret;}
};

下一道是力扣153. Find Minimum in Rota,这是一道中等题,前面的都还是简单题,既然是中等题,当然比简单题要难,果然,我尝试用左闭右闭的区间都失败了,最后只能用左闭右开,到底什么时候用左闭右闭,什么时候不能用呢?由于这道题也没有target,我就想着应该是和left和right比,但并不明白是为什么?而且这道题怎么不用ret?为什么最后又要返回nums[left]?

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

下一道是力扣162. Find Peak Element,这道题也是中等题,果然我的边界条件就错得离谱,这道题又需要用到ret了。

class Solution {
public:int findPeakElement(vector<int>& nums) {auto get=[&](int i)->pair<int,int>{if(i==-1 || i==nums.size()){return {0,0};}else{return {1,nums[i]};}};int left=0;int right=nums.size()-1;int ret;while(left<=right){int middle=left+(right-left)/2;if(get(middle+1)<get(middle) && get(middle-1)<get(middle)){ret=middle;break;}else if(get(middle)<get(middle+1)){left=middle+1;}else{right=middle-1;}}return ret;}
};

下一道,力扣33. Search in Rotated Sorte,是旋转数组,在旋转数组中找target,我是直接不知道和二分法有什么关系了,最后从别人的题解里面发现可以找到left到middle的顺序或者right到middle的顺序,可是我做题目的时候怎么想得起来?为什么我联想不到?联想到了又会划分错边界条件。

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

下一道,744. Find Smallest Letter G。感觉这道题和69题很像,除了if判断句条件以及yijiretret需要一个初始值之外几乎没有差别

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

最后一题是力扣441. Arranging Coins,它这道题也是,我无法用左闭右闭的区间去写,而且也用到了等差数列求和的一个问题,我一开始也是想不到,而且关于left的初始值以及middle的值的设置也是有很多小坑。并且left居然不可以等于middle+1,会超时,而且最后返回的值居然变成了left?为什么?

class Solution {
public:int arrangeCoins(int n) {int left=1;int right=n;while(left<right){int middle=left+(right-left+1)/2;if((long long)middle*(middle+1)<=(long long)2*n){left=middle;}else{right=middle-1;}}return left;}
};

二、总结

相信大家在刷二分法的时候也会遇到一些相似的问题,接下来就一一解答吧

1.ret什么时候使用

这个其实就是去看题目的要求,如果说题目只是要找到target的下标,那么可以不需要用到额外的变量去存储,但如果需要让我们搜索插入位置,又或者说寻找峰值的话,如果不用变量去保存的话,很容易就会丢失上一个数据。这个还是比较好判断到底需不需要用ret。完全可以先写出大概的代码大纲之后进行细节补充,然后发现需要用一个变量保存的时候再去添加一个变量就好了。当然了,要注意变量的初始值,可以回去看看题目要求,尤其是边界条件。

2.什么时候不能用左闭右闭

用几个示例代入代码,看是否会发生死循环,如果会,就说明不能用left<=right当做while循环的条件

3.返回值到底怎么判断是什么

其实也可以把一个实例带入代码,看最后究竟哪一个才是题目要得到的值。多做几道题之后就会有感觉了。

4.边界条件,越界

5.left和right到底怎么写?怎么有时候是=middle,有时候又是middle+1的

(1)区间问题,看自己的区间是左闭右闭还是左闭右开

(2)或者也可以用实例带入代码

三、题目分类

1.普通二分查找,求下标(或者其数值)

        35. 搜索插入位置

        704. 二分查找

2.二分求最小

3.二分求最大

4.其他

        162. 寻找峰值

       

四、不同题目分类的解法(待续)

这篇关于二分法的总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1055012

相关文章

Python实现图片分割的多种方法总结

《Python实现图片分割的多种方法总结》图片分割是图像处理中的一个重要任务,它的目标是将图像划分为多个区域或者对象,本文为大家整理了一些常用的分割方法,大家可以根据需求自行选择... 目录1. 基于传统图像处理的分割方法(1) 使用固定阈值分割图片(2) 自适应阈值分割(3) 使用图像边缘检测分割(4)

Windows Docker端口占用错误及解决方案总结

《WindowsDocker端口占用错误及解决方案总结》在Windows环境下使用Docker容器时,端口占用错误是开发和运维中常见且棘手的问题,本文将深入剖析该问题的成因,介绍如何通过查看端口分配... 目录引言Windows docker 端口占用错误及解决方案汇总端口冲突形成原因解析诊断当前端口情况解

java常见报错及解决方案总结

《java常见报错及解决方案总结》:本文主要介绍Java编程中常见错误类型及示例,包括语法错误、空指针异常、数组下标越界、类型转换异常、文件未找到异常、除以零异常、非法线程操作异常、方法未定义异常... 目录1. 语法错误 (Syntax Errors)示例 1:解决方案:2. 空指针异常 (NullPoi

Java反转字符串的五种方法总结

《Java反转字符串的五种方法总结》:本文主要介绍五种在Java中反转字符串的方法,包括使用StringBuilder的reverse()方法、字符数组、自定义StringBuilder方法、直接... 目录前言方法一:使用StringBuilder的reverse()方法方法二:使用字符数组方法三:使用自

Python依赖库的几种离线安装方法总结

《Python依赖库的几种离线安装方法总结》:本文主要介绍如何在Python中使用pip工具进行依赖库的安装和管理,包括如何导出和导入依赖包列表、如何下载和安装单个或多个库包及其依赖,以及如何指定... 目录前言一、如何copy一个python环境二、如何下载一个包及其依赖并安装三、如何导出requirem

Rust格式化输出方式总结

《Rust格式化输出方式总结》Rust提供了强大的格式化输出功能,通过std::fmt模块和相关的宏来实现,主要的输出宏包括println!和format!,它们支持多种格式化占位符,如{}、{:?}... 目录Rust格式化输出方式基本的格式化输出格式化占位符Format 特性总结Rust格式化输出方式

Python中连接不同数据库的方法总结

《Python中连接不同数据库的方法总结》在数据驱动的现代应用开发中,Python凭借其丰富的库和强大的生态系统,成为连接各种数据库的理想编程语言,下面我们就来看看如何使用Python实现连接常用的几... 目录一、连接mysql数据库二、连接PostgreSQL数据库三、连接SQLite数据库四、连接Mo

Git提交代码详细流程及问题总结

《Git提交代码详细流程及问题总结》:本文主要介绍Git的三大分区,分别是工作区、暂存区和版本库,并详细描述了提交、推送、拉取代码和合并分支的流程,文中通过代码介绍的非常详解,需要的朋友可以参考下... 目录1.git 三大分区2.Git提交、推送、拉取代码、合并分支详细流程3.问题总结4.git push

Kubernetes常用命令大全近期总结

《Kubernetes常用命令大全近期总结》Kubernetes是用于大规模部署和管理这些容器的开源软件-在希腊语中,这个词还有“舵手”或“飞行员”的意思,使用Kubernetes(有时被称为“... 目录前言Kubernetes 的工作原理为什么要使用 Kubernetes?Kubernetes常用命令总

Python中实现进度条的多种方法总结

《Python中实现进度条的多种方法总结》在Python编程中,进度条是一个非常有用的功能,它能让用户直观地了解任务的进度,提升用户体验,本文将介绍几种在Python中实现进度条的常用方法,并通过代码... 目录一、简单的打印方式二、使用tqdm库三、使用alive-progress库四、使用progres