【LeetCode Hot100 二分查找】搜索插入位置、搜索二维矩阵、搜索旋转排序数组、寻找两个正序数组的中位数

二分查找

    • 搜索插入位置
    • 搜索二维矩阵
    • 在排序数组中查找元素的第一个和最后一个位置
    • 寻找旋转排序数组中的最小值
    • 搜索旋转排序数组
    • 寻找两个正序数组的中位数(hard)

搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1

代码:
闭区间解法

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

搜索二维矩阵

给你一个满足下述两条属性的 m x n 整数矩阵:

  • 每行中的整数从左到右按非严格递增顺序排列。
  • 每行的第一个整数大于前一行的最后一个整数。

给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。
在这里插入图片描述
思路
把该二维矩阵设想成一个一维的有序数组,那么在该一维数组的第 i i i 个位置的数可以用二维矩阵 ( m 行 n 列) 表示,即在 i / n i/n i/n 行, i % n i\%n i%n 列.

代码
在上一题的基础上修改代码:

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        int n = matrix[0].length;
        int left = 0, right = m*n-1;
        while(left <= right) {
            int mid = left + (right - left) / 2;
            int val = matrix[mid/n][mid%n];
            if (val == target) {
                return true;
            } else if(val < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return false;
    }
}

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

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

思路
用两次二分查找分别找左边界和有边界
第一次二分法找左边界,第二次二分法找右边界

代码
先初始化左边界有边界为 -1

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        int leftBoard = -1, rightBoard = -1;
        // 寻找左边界
        while(left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) {
                leftBoard = mid; 
                right = mid - 1;  // 找到之后 在 mid 的左边区间继续找,直到找到最左边的边界
            } else if(nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        left = 0;
        right = nums.length - 1;
        // 寻找右边界
        while(left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) {
                rightBoard = mid;
                left = mid + 1;
            } else if(nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return new int[]{leftBoard, rightBoard};
    }
}

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

已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。
给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。
示例 2:
输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 3 次得到输入数组。

思路
设 x=nums[mid] 是现在二分取到的数。
我们需要判断 x 和数组最小值的位置关系,谁在左边,谁在右边?
把 x 与最后一个数 nums[n−1] 比大小:

  • 如果 x>nums[n−1],那么可以推出以下结论:
    • nums 一定被分成左右两个递增段;
    • 第一段的所有元素均大于第二段的所有元素;
    • x 在第一段。
    • 最小值在第二段。
    • 所以 x 一定在最小值的左边。
  • 如果 x≤nums[n−1],那么 x 一定在第二段。(或者 nums 就是递增数组,此时只有一段。)
    • x 要么是最小值,要么在最小值右边。

所以,只需要比较 x 和 nums[n−1] 的大小关系,就间接地知道了 x 和数组最小值的位置关系,从而不断地缩小数组最小值所在位置的范围,二分找到数组最小值。

代码

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

搜索旋转排序数组

整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1

思路
使用上一题的思路,先找到该旋转排序数组的最小值的位置,然后根据 target 和 旋转的位置(旋转排序数组的最后一个数)的大小进行比较,判断是在左边查找还是在右边查找。

代码

class Solution {
    public int search(int[] nums, int target) {
        int min = findMin(nums);  // 先找到最小值的下标
        int n = nums.length;
        if (target == nums[n -1]) {
            return n - 1;   // 相等的话直接返回
        } else if (target > nums[n-1]) {
            return searchTarget(nums, target, 0, min - 1);  // 在左边查找
        } else { 
            return searchTarget(nums, target, min, n - 2);  // 在右边查找
        }
    }
	// 查找最小值下标
    public int findMin(int[] nums) {
        int n = nums.length;
        int left = 0, right = n - 2;
        while( left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > nums[n - 1]) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return left;
    }
	// 二分查找
    public int searchTarget(int[] nums, int target, int left, int right) {
        int n = nums.length;
        while(left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] > target) {
                right = mid - 1; 
            } else {
                left = mid + 1;
            }
        }
        return -1;
    }
}

寻找两个正序数组的中位数(hard)

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

思路
我们将通过 二分查找 来解决这个问题,具体步骤如下:

  1. 确保 nums1 是较短的数组
  • 由于我们要在较短的数组上进行二分查找,为了减少计算复杂度,我们首先确保 nums1 的长度小于或等于 nums2。如果 nums1 的长度大于 nums2,我们交换这两个数组。
  • 这样做的目的是保证二分查找的次数最小化,最大化效率。
  1. 分割数组
  • 我们的目标是将两个数组分割成左右两部分,使得合并后的两个部分的元素个数相同,或者左边多一个元素(如果总长度是奇数)。具体来说,假设 nums1 的长度是 n,nums2 的长度是 m,则:
    • 左边部分的元素个数应为 (n + m + 1) / 2,这个值可以保证左边部分最多比右边部分多一个元素(当总长度是奇数时)。
    • 右边部分的元素个数为 n + m - (n + m + 1) / 2。
  1. 二分查找
  • 在 nums1 上执行二分查找,假设当前查找的分割位置是 partition1,那么 nums1 的左边部分就是 nums1[0]…nums1[partition1-1],右边部分是 nums1[partition1]…nums1[n-1]。
  • 同样地,nums2 的分割位置 partition2 可以通过以下公式计算:
    p a r t i t i o n 2 = ( n + m + 1 ) / 2 − p a r t i t i o n 1 partition2= (n+m+1)/2 −partition1 partition2=(n+m+1)/2partition1
    这个公式确保了左边部分的总元素个数为 (n + m + 1) / 2。
  1. 确保分割符合条件
  • 为了保证左边部分的所有元素都小于或等于右边部分的所有元素,我们需要检查:
    • nums1[partition1 - 1] <= nums2[partition2](nums1 左边的最大值小于 nums2 右边的最小值)。
    • nums2[partition2 - 1] <= nums1[partition1](nums2 左边的最大值小于 nums1 右边的最小值)。
      如果这些条件成立,说明我们找到了合适的分割位置。
  1. 计算中位数
  • 如果两个数组的总长度是奇数,中位数就是左边部分的最大值,max(nums1[partition1 - 1], nums2[partition2 - 1])。
  • 如果两个数组的总长度是偶数,中位数是左边部分的最大值和右边部分的最小值的平均值:
    m e d i a n = ( m a x ( n u m s 1 [ p a r t i t i o n 1 − 1 ] , n u m s 2 [ p a r t i t i o n 2 − 1 ] ) + m i n ( n u m s 1 [ p a r t i t i o n 1 ] , n u m s 2 [ p a r t i t i o n 2 ] ) ) / 2 median = (max(nums1[partition1−1],nums2[partition2−1])+min(nums1[partition1],nums2[partition2])) / 2 median=(max(nums1[partition11],nums2[partition21])+min(nums1[partition1],nums2[partition2]))/2
  1. 二分查找的调整
  • 如果不满足条件,意味着 partition1 需要调整:
    • 如果 nums1[partition1 - 1] > nums2[partition2],则需要将 partition1 向左移,即减小 partition1。
    • 如果 nums2[partition2 - 1] > nums1[partition1],则需要将 partition1 向右移,即增大 partition1。
  1. 边界条件
  • 对于数组的边界,我们使用 Integer.MIN_VALUE 和 Integer.MAX_VALUE 来处理数组分割的边界情况。例如,如果 partition1 为 0,意味着 nums1 左边部分没有元素,我们将 maxLeft1 设置为 Integer.MIN_VALUE;如果 partition1 为 n,意味着 nums1 右边部分没有元素,我们将 minRight1 设置为 Integer.MAX_VALUE。

代码

public class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        // 保证 nums1 是较短的数组
        if (nums1.length > nums2.length) {
            int[] temp = nums1;
            nums1 = nums2;
            nums2 = temp;
        }

        int n = nums1.length;
        int m = nums2.length;

        // 在 nums1 上进行二分查找
        int left = 0, right = n;

        while (left <= right) {
            int partition1 = (left + right) / 2;
            int partition2 = (n + m + 1) / 2 - partition1;

            // 获取 nums1 和 nums2 中的元素
            int maxLeft1 = (partition1 == 0) ? Integer.MIN_VALUE : nums1[partition1 - 1];
            int minRight1 = (partition1 == n) ? Integer.MAX_VALUE : nums1[partition1];

            int maxLeft2 = (partition2 == 0) ? Integer.MIN_VALUE : nums2[partition2 - 1];
            int minRight2 = (partition2 == m) ? Integer.MAX_VALUE : nums2[partition2];

            // 检查是否找到合适的分割位置
            if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1) {
                // 如果数组长度是奇数
                if ((n + m) % 2 == 1) {
                    return Math.max(maxLeft1, maxLeft2);
                } else {
                    // 如果数组长度是偶数
                    return (Math.max(maxLeft1, maxLeft2) + Math.min(minRight1, minRight2)) / 2.0;
                }
            } else if (maxLeft1 > minRight2) {
                // 如果 maxLeft1 太大,调整左边界
                right = partition1 - 1;
            } else {
                // 如果 maxLeft2 太大,调整右边界
                left = partition1 + 1;
            }
        }

        return 0.0;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT 主流模型GPT-4/GPT-4o mini的参数规模是多大?

微软论文又把 OpenAI 的机密泄露了&#xff1f;&#xff1f;在论文中明晃晃写着&#xff1a; o1-preview 约 300B&#xff1b;o1-mini 约 100BGPT-4o 约 200B&#xff1b;GPT-4o-mini 约 8BClaude 3.5 Sonnet 2024-10-22 版本约 175B微软自己的 Phi-3-7B&#xff0c;这个不用约…

Docker 安装Elasticsearch搜索引擎 搜索优化 词库挂载 拼音分词 插件安装

介绍 允许用户快速索引和搜索大量的文本数据。通过使用倒排索引&#xff0c;它能够在海量数据中高效检索相关信息。提供灵活的查询语言&#xff0c;可以做全文搜索、模糊搜索、数据统计等&#xff0c;用来代替MYSQL的模糊搜索&#xff0c;MYSQL的模糊搜索不支持使用索引从而导…

Scala_【5】函数式编程

第五章 函数式编程函数和方法的区别函数声明函数参数可变参数参数默认值 函数至简原则匿名函数高阶函数函数作为值传递函数作为参数传递函数作为返回值 函数闭包&柯里化函数递归控制抽象惰性函数友情链接 函数式编程 面向对象编程 解决问题时&#xff0c;分解对象&#xff…

jenkins入门7 --发送邮件1

jenkins发送邮件配置&#xff08;全局配置&#xff09;_jenkins 怎么发送邮件-CSDN博客 本文通过163发送邮件 1、首先163设置选择pop3/smtp/imap,开启服务&#xff0c;获取授权码 2、jenkins下载邮件插件 登录Jenkins管理界面&#xff0c;点击“Manage Jenkins”。 选择“Man…

git 常用命令和本地合并解决冲突

目录 一、常用命令 二、本地可视化合并分支解决冲突 一、常用命令 最近&#xff0c;使用mac电脑&#xff0c;无法直接使用小乌龟进行可视化操作&#xff0c;现在记录一些常用命令。 拉取&#xff1a; git clone <git url> 仅拉起某个单独分支&#xff1a; git clo…

彻底学会Gradle插件版本和Gradle版本及对应关系

看完这篇&#xff0c;保你彻底学会Gradle插件版本和Gradle版本及对应关系&#xff0c;超详细超全的对应关系表 需要知道Gradle插件版本和Gradle版本的对应关系&#xff0c;其实就是需要知道Gradle插件版本对应所需的gradle最低版本&#xff0c;详细对应关系如下表格&#xff0…

我的创作纪念日——《惊变128天》

我的创作纪念日——《惊变128天》 机缘收获日常成就憧憬 机缘 时光飞逝&#xff0c;转眼间&#xff0c;我已在这条创作之路上走过了 128 天。回顾起 2024 年 8 月 29 日&#xff0c;我满怀忐忑与期待&#xff0c;撰写了第一篇技术博客《讲解LeetCode第1题&#xff1a;两数之和…

医学图像分析工具02:3D Slicer || 医学影像可视化与分析工具 支持第三方插件

3D Slicer 是一款功能全面的开源医学影像分析软件&#xff0c;广泛应用于影像处理、三维建模、影像配准和手术规划等领域。它支持多种医学影像格式&#xff08;如 DICOM、NIfTI&#xff09;和丰富的插件扩展&#xff0c;是神经科学、放射学和生物医学研究中不可或缺的工具。 在…

【每日学点鸿蒙知识】Hap 安装失败、ArkTS 与C++ 数组转换、渐变遮罩效果等

1、在启动调试或运行应用/服务时&#xff0c;安装HAP出现错误&#xff0c;提示“error: install failed due to older sdk version in the device”错误信息。 这是由于编译打包所使用的SDK版本与设备镜像版本不匹配。不匹配的场景包括&#xff1a; 场景一&#xff1a;设备上…

分布式搜索引擎之elasticsearch基本使用3

分布式搜索引擎之elasticsearch基本使用3 1.部署单点es 1.1.创建网络 因为我们还需要部署kibana容器&#xff0c;因此需要让es和kibana容器互联。这里先创建一个网络&#xff1a; docker network create es-net1.2.加载镜像 这里我们采用elasticsearch的7.12.1版本的镜像&…

在macOS上安装MySQL

macOS的MySQL有多种不同的形式&#xff1a; 1、本机包安装程序&#xff0c;它使用本机macOS安装程序&#xff08;DMG&#xff09;引导您完成MySQL的安装。有关详细信息&#xff0c;请参阅第2.4.2节&#xff0c;“使用本机包在macOS上安装MySQL”。您可以将包安装程序与macOS一…

Apache HTTPD 换行解析漏洞(CVE-2017-15715)

漏洞简介 pache HTTPD是一款HTTP服务器&#xff0c;它可以通过mod_php来运行PHP网页。其2.4.0~2.4.29版本中存在一个解析漏洞&#xff0c;在解析PHP时&#xff0c;1.php\x0A将被按照PHP后缀进行解析&#xff0c;导致绕过一些服务器的安全策略。 漏洞环境 vulhub/httpd/CVE-2…

jenkins入门4 --window执行execute shell

1、启动关闭jenkins 在Windows环境下&#xff0c;如果你需要关闭Jenkins服务&#xff0c;可以通过以下几种方式&#xff1a; 1、使用Windows服务管理器&#xff1a; 打开“运行”对话框&#xff08;Win R&#xff09;&#xff0c;输入services.msc&#xff0c;然后回车。 在服…

conda安装及demo:SadTalker实现图片+音频生成高质量视频

1.安装conda 下载各个版本地址&#xff1a;https://repo.anaconda.com/archive/ win10版本&#xff1a; Anaconda3-2023.03-1-Windows-x86_64 linux版本&#xff1a; Anaconda3-2023.03-1-Linux-x86_64 Windows安装 环境变量 conda -V2.配置conda镜像源 安装pip conda…

医学图像分析工具01:FreeSurfer || Recon -all 全流程MRI皮质表面重建

FreeSurfer是什么 FreeSurfer 是一个功能强大的神经影像学分析软件包&#xff0c;广泛用于处理和可视化大脑的横断面和纵向研究数据。该软件由马萨诸塞州总医院的Martinos生物医学成像中心的计算神经影像实验室开发&#xff0c;旨在为神经科学研究人员提供一个高效、精确的数据…

vite打包报错“default“ is not exported by “node_modules/dayjs/dayjs.min.js“

vite打包最开始报的错是&#xff1a; 查找各种解决办法后&#xff0c;第一次尝试如下&#xff1a; npm i rollup/plugin-commonjs npm i vite-plugin-require-transform但继续报错&#xff1a; 最后解决办法为&#xff1a; 忽略掉node_modules 在vite.config.ts里修改代码 …

医院管理住院系统的研究与实现

第三章 系统的需求分析和可行性研究 3.1 功能需求 经过对本系统的研究分析&#xff0c;本系统主要是为了方便让医院更快捷的管理。所面向的对象主要有病人、医生和医院的管理人员。病人运用该系统后&#xff0c;可以根据该系统查看自己所需要的信息&#xff0c;包括治疗自己…

安徽省地图arcgis数据美化后mxd文件shp格式下载后内容测评

标题中的“安徽省地图arcgis数据美化后mxd文件shp格式”揭示了这个压缩包的内容是经过GIS处理的、针对安徽省地图数据。ArcGIS是一款由Esri公司开发的专业地理信息系统软件&#xff0c;用于处理、分析和展示地理空间数据。MXD文件是ArcGIS的项目文件&#xff0c;包含了地图布局…

GitLab创建用户,设置访问SSH Key

继上一篇 Linux Red Hat 7.9 Server安装GitLab-CSDN博客 安装好gitlab&#xff0c;启用管理员root账号后&#xff0c;开始创建用户账户 1、创建用户账户 进入管理后台页面 点击 New User 输入用户名、邮箱等必填信息和登录密码 密码最小的8位&#xff0c;不然会不通过 拉到…

计算机网络--根据IP地址和路由表计算下一跳

一、必备知识 1.无分类地址IPV4地址网络前缀主机号 2.每个IPV4地址由32位二进制数组成 3. /15这个地址表示网络前缀有15位&#xff0c;那么主机号32-1517位。 4.地址掩码&#xff08;子网掩码&#xff09;&#xff1a;所对应的网络前缀为1&#xff0c;主机号为0。 5.计算下…