六、二分搜索-算法总结

news2024/9/19 17:33:59

文章目录

  • 六、二分搜索
    • 6.1 简介
    • 6.2 典型实例 -- 二分查找
    • 6.2 模板
    • 6.3 常见题目
      • 6.3.1 搜索插入位置
      • 6.3.2 搜索二维矩阵
      • 6.3.3 寻找旋转排序中数组中的最小值
      • 6.3.4 寻找旋转排序数组中的最小值 II
      • 6.3.5 搜索旋转排序数组
      • 6.3.6 搜索旋转排序数组 II
  • 总结

六、二分搜索

6.1 简介

给一个有序数组和目标值,找第一次/最后一次/任何一次出现的索引,如果没有出现返回-1
模板四点要素

  • 1、初始化
  • 2、循环退出条件
  • 3、比较中点和目标值
  • 4、判断最后两个元素是否符合:A[mid] ==、<、> target
    时间复杂度O(logn),使用场景一般是有序数组的查找

6.2 典型实例 – 二分查找

704. 二分查找
在这里插入图片描述

class Solution {
    // 二分搜索最常用模板
    public int search(int[] nums, int target) {
        // method1: 基于迭代
        // int left = 0, right = nums.length-1;
        // while (left<=right){
        //     int mid = (left+right)/2;
        //     if(target > nums[mid]){
        //         left = mid+1;
        //     }else if(target < nums[mid]){
        //         right = mid-1;
        //     }else{
        //         return mid;
        //     }
        // }
        // return -1;
        // method2: 基于递归
        return search(0, nums.length - 1, nums, target);
    }

    private int search(int left, int right, int[] nums, int target) {
        if (left > right) {
            return -1;
        }
        int mid = (left + right) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] > target) {
            return search(left, mid - 1, nums, target);
        } else {
            return search(mid + 1, right, nums, target);
        }

    }
}

6.2 模板

大部分二分查找类的题目都可以用这个模板,然后做一点特殊逻辑即可
另外二分查找还有一些其他模板如下图,大部分场景模板#3 都能解决问题,而且还能找第一次/最后一次出现的位置,应用更加广泛
在这里插入图片描述

所以用模板#3就对了,详细的对比可以这边文章介绍:二分搜索模板
如果是最简单的二分搜索,不需要找第一个、最后一个位置、或者是没有重复元素,可以使用模板#1,代码更简洁常见题目

6.3 常见题目

6.3.1 搜索插入位置

35. 搜索插入位置
在这里插入图片描述

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

        // 对最后两个结果进行判断 (此时:left+1=right)
        // target逻辑上最终的落点:负无穷,left,right,正无穷
        if(nums[left] >= target){
            return left;
        }else if(nums[right] >= target){
            return right;
        }else{
            return right+1;
        }
    }
}

6.3.2 搜索二维矩阵

74. 搜索二维矩阵
在这里插入图片描述

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        // 先定位在哪一行
        int raw = 0;
        for(int i = 0;i<matrix.length;i++){
            if(target >= matrix[i][0] && target <= matrix[i][matrix[i].length-1]){
                if(target == matrix[i][0] || target == matrix[i][matrix[i].length-1]){
                    return true;
                }
                raw = i;
                break;
            }
        }

        // 在定位中搜索目标值
        int[] raws = matrix[raw];
        int left = 0, right = raws.length-1;
        while(left<=right){
            int mid = left + (right - left) / 2;
            if(raws[mid] == target){
                return true;
            }else if(raws[mid] > target){
                right = mid -1;
            }else{
                left = mid + 1;
            }
        }
        return false;
    }
}

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

寻找旋转排序数组中的最小值
在这里插入图片描述

在这里插入图片描述

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

6.3.4 寻找旋转排序数组中的最小值 II

154. 寻找旋转排序数组中的最小值 II
在这里插入图片描述

在这里插入图片描述

// method1: 若最小值出现不止一次,此方法定位的最小值位置不能确定是否是原单调递增的最左侧的数据位置
class Solution {
    public int findMin(int[] nums) {
        if(nums.length == 1){
            return nums[0];
        }
        int left = 0, right = nums.length - 1;
        while(left + 1 < right){
            int mid = left + (right - left) / 2;
            if(nums[mid] < nums[right]){
                right = mid;
            }else if(nums[mid] > nums[right]){
                left = mid;
            }else{
                right--;
            }
        }
        return Math.min(nums[left], nums[right]);
    }
}
// method2: 若最小值出现不止一次,此方法定位的最小值位置是原单调递增的最左侧的数据位置
class Solution {
    public int findMin(int[] nums) {
        if(nums.length == 1){
            return nums[0];
        }
        int left = 0, right = nums.length - 1;
        while(left + 1 < right){
            while(left<right && nums[left] == nums[left+1]){ // 去除左侧重复数字(仅保留一个)
                left++;
            }
            while(left<right && nums[right] == nums[right-1]){ // 去除右侧重复数字(仅保留一个)
                right--;
            }
            int mid = left + (right - left) / 2;
            if(nums[mid] < nums[right]){
                right = mid;
            }else if(nums[mid] > nums[right]){
                left = mid;
            }
        }
        return Math.min(nums[left], nums[right]);
    }
}

6.3.5 搜索旋转排序数组

33.搜索旋转排序数组
在这里插入图片描述

// method1: 基于两趟搜索
// 时间复杂度 O(logn)
class Solution {
    public int search(int[] nums, int target) {
        if(nums.length == 1){
            return nums[0]==target?0:-1;
        }
        // 找到最小值所在位置
        // 其左侧(若存在)是递增的
        // 其右侧(包括它自己)是递增的
        int left = 0, right = nums.length - 1;
        while(right - left > 1){
            int mid = left + (right - left) / 2;
            if(nums[mid] < nums[right]){
                right = mid;
            }else{
                left = mid;
            }
        }
        // 定位最小值的位置
        int minIndex = nums[left] < nums[right] ? left:right;
        // target 可能存在于右边
        if(target>=nums[minIndex] && target<=nums[nums.length-1]){
            return search(nums, minIndex, nums.length-1,target);
        }
        // target 可能存在于左边(注意:nums本身为递增数组)
        if(minIndex != 0 && (target>=nums[0] && target<=nums[minIndex-1])){
            return search(nums, 0, minIndex-1,target);
        }
        return -1;
    }
    private int search(int[] nums,int left, int right, int target){
        if(right < 0){
            return -1;
        }
        while(left+1<right){
            int mid = left + (right-left) / 2;
            if(nums[mid] == target){
                return mid;
            }else if(nums[mid] < target){
                left = mid;
            }else{
                right = mid;
            }
        }
        if(nums[left] == target){
            return left;
        }else if(nums[right] == target){
            return right;
        }
        return -1;
    }
}
// method2: 基于一趟搜索
// 时间复杂度 O(logn)
class Solution {
    public int search(int[] nums, int target) {
        if(nums.length == 1){
            return nums[0]==target?0:-1;
        }
        int left = 0, right = nums.length - 1;
        while(right - left > 1){
            int mid = left + (right - left) / 2;
            if(nums[mid] == target){
                return mid;
            }
            if(nums[mid] > nums[left]){ // 位于左边较大的单调区间
                if(target >= nums[left] && target < nums[mid]){
                    right = mid; // 位于闭区间 [left, mid]
                }else{ 
                // 位于开区间 [-无穷, left] or [mid, 当前区间的右边界] == [mid, right]
                // 注意[-无穷, left]就是其右侧的较小单调区间
                    left = mid;
                }
            }
            if(nums[mid] < nums[right]){ // 位于右边较小的单调区间
                if(target > nums[mid] && target <= nums[right]){
                    left = mid; // 位于闭区间 [mid, right]
                }else{ 
                // 位于开区间 [当前区间的左边界, mid] or [right, +无穷] == [left, mid]
                // 注意[right, +无穷]就是其左侧的较大单调区间
                    right = mid;
                }
            }
        }
        // 对最后的两个数据进行判断
        if(nums[left] == target){
            return left;
        }else if(nums[right] == target){
            return right;
        }
        return -1;
    }

}

6.3.6 搜索旋转排序数组 II

搜索旋转排序数组 II
在这里插入图片描述

// mthod1: 基于两趟搜索
// 时间复杂度:O(logn)
class Solution {
    public boolean search(int[] nums, int target) {
        // step 1: 定位最小值位置(需要定位原单调递增的最左侧的值的位置)
        int left = 0, right = nums.length - 1;
        while(right - left > 1){
            while(left < right && nums[left] == nums[left+1]){ // 去除左侧重复的数字
                left++;
            }
            while(left < right && nums[right] == nums[right-1]){ // 去除右侧重复的数字
                right--;
            }
            int mid = left + (right - left) / 2;
            if(nums[mid] < nums[right]){
                right = mid;
            }else{
                left = mid;
            }
        }
        int minIndex = (nums[left] <= nums[right] ? left:right);

        // step: 判断目标值位于哪个区间中查询
        if(target>=nums[minIndex] && target <= nums[nums.length - 1]){
            return search(nums, minIndex, nums.length - 1, target);
        }
        if(minIndex !=0 && target >= nums[0] && target <= nums[minIndex-1]){
            return search(nums, 0, minIndex - 1, target);
        }
        return false;
    }

    private boolean search(int[] nums, int left, int right, int target){
        while(right - left > 1){
            int mid = left + (right - left) / 2;
            if(nums[mid] == target){
                return true;
            }else if(nums[mid] < target){
                left = mid;
            }else{
                right = mid;
            }
        }
        if(nums[left] == target || nums[right] == target){
            return true;
        }
        return false;
    }
}
// mthod2: 基于一趟搜索
// 时间复杂度:O(logn)
class Solution {
    public boolean search(int[] nums, int target) {
        if(nums.length == 1){
            return nums[0] == target;
        }

        int left = 0, right = nums.length - 1;
        while(right - left > 1){

            while(left<right && nums[left] == nums[left+1]){
                left++;
            }
            while(left<right && nums[right] == nums[right-1]){
                right--;
            }

            int mid = left + (right - left) / 2;
            if(nums[mid] == target){
                return true;
            }

            // 当前mid位于左侧区间
            if(nums[mid] > nums[left]){
                if(target>=nums[left] && target < nums[mid]){
                    right = mid;
                }else{
                    left = mid;
                }
            }
            // 当前mid位于右侧区间
            if(nums[mid] < nums[right]){
                if(target > nums[mid] && target <= nums[right]){
                    left = mid;
                }else{
                    right = mid;
                }
            }
        }
        if(nums[left] == target || nums[right] == target){
            return true;
        }
        return false;

    }
}

总结

二分搜索四点要素(必背&理解)

  • 1、初始化:start=0、end=len-1
  • 2、循环退出条件:start + 1 < end
  • 3、 比较中间点和目标值:A[mid] ==、<、> target
  • 4、判断最后两个元素是否符合:A[start]、A[end] ? target

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

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

相关文章

Java或者前端 实现中文排序(调API的Demo)

目录 前言1. 前端2. Java 前言 前端 Vue 中的中文排序通常使用 JavaScript 提供的 localeCompare 方法来比较中文字符串 Java 后端可以使用 Collator 类来实现中文排序 1. 前端 在 Vue 中&#xff0c;使用 localeCompare 来实现中文字符串的排序&#xff1a; <template&…

Skyeye 云智能制造 v3.14.5 发布,ERP 商城

Skyeye 云智能制造&#xff0c;采用 Springboot winUI 的低代码平台、移动端采用 UNI-APP。包含 30 多个应用模块、50 多种电子流程&#xff0c;CRM、PM、ERP、MES、ADM、EHR、笔记、知识库、项目、门店、商城、财务、多班次考勤、薪资、招聘、云售后、论坛、公告、问卷、报表…

SAM 2: Segment Anything in Images and Videos

SAM2: 在图像和视频中分割任何内容 作者 Nikhila Ravi, Valentin Gabeur, Yuan-Ting Hu, Ronghang Hu 等 (Meta FAIR) 论文&#xff1a;SAM2: Segment Anything in Images and Videos项目代码&#xff1a;GitHub Repository互动演示&#xff1a;Demo 摘要 SAM2 是一个用于处…

基于51单片机的锅炉温度控制系统PID调节proteus仿真

地址&#xff1a; https://pan.baidu.com/s/17oMgAnUBUKKEVnv5hNRQmQ 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectro…

招聘数据分析师,HR会考察候选人哪些方面?

数据分析是必须具备深厚的专业技能底蕴&#xff0c;这是最基本的要求&#xff0c;其中包括对数据分析工具和编程语言的精通&#xff0c;以及对数据库管理的实验操作。 在数据分析师的招聘过程中&#xff0c;必须要注重对方掌握的知识&#xff0c;比如数据挖掘数据可视化等先进…

Qt构建JSON及解析JSON

目录 一.JSON简介 JSON对象 JSON数组 二.Qt中JSON介绍 QJsonvalue Qt中JSON对象 Qt中JSON数组 QJsonDocument 三.Qt构建JSON数组 四.解析JSON数组 一.JSON简介 一般来讲C类和对象在java中是无法直接直接使用的&#xff0c;因为压根就不是一个规则。但是他们在内存中…

详解:Tensorflow、Pytorch、Keras(搭建自己的深度学习网络)

这是一个专门对Tensorflow、Pytorch、Keras三个主流DL框架的一个详解和对比分析 一、何为深度学习框架&#xff1f; 你可以理解为一个工具帮你构建一个深度学习网络&#xff0c;调用里面的各种方法就能自行构建任意层&#xff0c;diy你想要的DNN&#xff0c;而且任意指定学习…

揭秘!当业务方需求模糊,产品经理如何施展‘化雾为金’的神奇策略!

引言 在产品管理的实践中&#xff0c;产品经理经常会遇到业务方无法清晰表达需求的情况。这可能是由于业务方对问题的理解不够深入&#xff0c;或者缺乏将业务需求转化为产品需求的经验。作为资深产品经理&#xff0c;我们需要采取一系列策略来应对这一挑战&#xff0c;确保产…

Solid Converter PDF9.1安装教程

软件介绍 Solid Converter PDF是一套专门将PDF文件转换成word的软件&#xff0c;除了转换成word文件外&#xff0c;还可以转换成RTF以及Word XML文件。除此之外&#xff0c;它还有一个图片撷取功能&#xff0c;可以让我们]将PDF档里的图片撷取出来&#xff0c;以及将PDF档里的…

word文档的读入(8)

如何读取答题卡中的选择题答案&#xff0c;并把所有的信息导入到Excel表格中&#xff5e; 在初始化了字典中的字段并获取了标准答案和学生答案后&#xff0c;现在只需使用if语句将学生答案studentAnswerOne和标准答案value进行比较。选择题一道题2分&#xff0c;答案正确时&…

C++11 右值引用完美转发

前言 上一期介绍了C11的基础新特性&#xff0c;这一期开始我们继续来介绍C11的新特性&#xff01; 目录 前言 一、右值引用和移动语义 1.1左值和左值引用 1.2右值和右值引用 1.3左值引用vs右值引用 1.4右值引用的使用场景和意义 左值引用的使用场景 左值引用的缺陷 …

应用案例|开源 PolarDB-X 在互联网安全场景的应用实践

背景介绍 中盾数科集团始创于2012年&#xff0c;是由网络安全服务而发展起来的科技型、多元化的企业集团。旗下包括网络安全服务、信创一体化服务、箱式液冷、区块链、位置服务、视觉服务等六大板块&#xff0c;业务覆盖湖南、甘肃、贵州等多个省份。 业务挑战 中盾集团基于A…

LabVIEW提高开发效率技巧----模块化设计与代码复用

模块化设计是现代软件开发中不可或缺的理念&#xff0c;在LabVIEW开发中尤为重要。通过将系统功能划分为独立的、可复用的模块&#xff0c;开发者可以显著提升代码的可读性、维护性以及效率。下面将介绍模块化设计的核心概念、如何利用子VI实现代码复用、以及使用模板&#xff…

claude,gpt,通义千问

起因是通义千问对这张图的解释有严重错误&#xff0c;因此想着让claude和gpt回答一下&#xff0c;记录一下。 claude 这张图表显示了中国多个城市的房地产价格指数数据。让我为您解读一下主要信息&#xff1a; 指标解释&#xff1a; "环比": 与上月相比的价格变化&…

[yotroy.cool] MGT 388 - Finance for Engineers - notes 笔记

个人博客https://www.yotroy.cool/,感谢关注~ 图片资源可能显示不全,请前往博客查看哦! ============================================================ Lecture 1 What is Accounting? The process of identifying, measuring and communicating economic informati…

【UEFI基础】BIOS下的启动项管理

启动管理 启动管理&#xff08;Boot Manager&#xff09;是UEFI BIOS中重要的一部分&#xff0c;它通过一系列的变量来确定启动策略&#xff0c;包括&#xff1a; 执行启动还是恢复操作启动顺序是如何 本文会介绍下面的内容&#xff1a; 与启动管理相关的变量启动或恢复的流…

Git rebase 的使用(结合图与案例)

目录 Git rebase 的使用Git rebase 概念Git rebase 原理rebase和merge的选择 Git rebase 的使用 在 Git 中整合来自不同分支的修改主要有两种方法&#xff1a;merge 以及 rebase Git rebase 概念 **rebase概念&#xff1a;**用来重新应用提交&#xff08;commits&#xff09…

Vue2知识点

注意:笔记内容来自网络 1Vue指令 指令是指&#xff1a;带有v-前缀的特殊标签属性 1.1 v-html v-html&#xff08;类似 innerHTML&#xff09; 使用语法&#xff1a;<p v-html"intro">hello</p>&#xff0c;意思是将 intro 值渲染到 p 标签中 类似 i…

神经网络-MNIST数据集训练

文章目录 一、MNIST数据集1.数据集概述2.数据集组成3.文件结构4.数据特点 二、代码实现1.数据加载与预处理2. 模型定义3. 训练和测试函数4.训练和测试结果 三、总结 一、MNIST数据集 MNIST数据集是深度学习和计算机视觉领域非常经典且基础的数据集&#xff0c;它包含了大量的手…

链表的合并,结点逆置,顺序表的高效划分(数据结构作业02)

目录 链表的合并 链表的结点逆置 顺序表的高效划分 链表的合并 已知两个递增有序的单链表A和B&#xff0c;分别表示两个集合。试设计一个算法&#xff0c;用于求出A与B的交集&#xff0c;并存储在C链表中。例如 : La {2&#xff0c;4&#xff0c;6&#xff0c;8}&#xff1b;…