【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;}
}

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

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

相关文章

后端java开发路由接口并部署服务器(四)

一、安装IntelliJ IDEA&#xff0c;安装包下载 1、官网下载 2、网盘资源 安装包下载完成后进行傻瓜式下一步安装就可以了 打开IntelliJ IDEA&#xff0c;输入网盘资源文件内容 三、汉化处理 插件搜索chinese&#xff0c;就会找到相应的插件安装重启软件即可 四、新建后端j…

Vue.js 表单验证实战:一个简单的登录页面

修改日期备注2025.1.2初版 一、前言 Vue.js 学习第一天——学会一个带有简单表单验证的登录页面。通过这个项目&#xff0c;会对 Vue.js 的核心概念有了更深入的理解&#xff0c;加深掌握如何运用 Vue 的一些强大特性来实现动态交互和数据处理。 二、项目的基本结构 首先&a…

国产文本编辑器EverEdit - 批量转码转换行符

1 批量转码&转换行符 1.1 应用场景 如果用户批量在Windows编辑文件&#xff0c;要上传到异构系统&#xff0c;如&#xff1a;Linux&#xff0c;则需要批量转换编码和换行符&#xff0c;此时可以使用EverEdit的批量转码功能。 1.2 使用方法 选择主菜单文档 -> 批量转码…

Java实现下载excel模板,并实现自定义下拉框

GetMapping("excel/download")ApiOperation(value "模板下载")public void getUserRecordTemplate(HttpServletResponse response, HttpServletRequest request) throws IOException {OutputStream outputStream response.getOutputStream();InputStream…

成立一家无人机培训机构需要哪些基础配置

成立一家无人机培训机构&#xff0c;需要一系列基础配置来确保教学质量、学员安全以及机构的正常运营。以下是根据公开发布的信息整理出的关键基础配置&#xff1a; 一、场地配置 1. 飞行场&#xff1a;提供一个安全、宽敞的室外飞行环境&#xff0c;面积最好大于三千平米&…

读“将计算性能调高到极致的基点秘诀”的尝试

看到一篇文章&#xff0c;说最近阅读LAMMPS源码&#xff0c;悟出了很多道理。在计算性能优化这块&#xff0c;源代码作者很多写法我最初不以为意&#xff0c;后来发现是作者有意为之&#xff0c;就是为了把计算性能优化到极致。做计算仿真软件&#xff0c;也特别需要注意这些吧…

端口被占用

端口8080被占用 哈哈哈&#xff0c;我是因为后端项目跑错了&#xff0c;两个项目后端名称太像了&#xff1b; &#xff08;1&#xff09;netstat -aon | findstr 8080&#xff0c;找到占用8080端口的进程号&#xff0c;获取对应的进程号pid&#xff1b; &#xff08;2&#…

文件本地和OSS上传

这里写目录标题 前端传出文件后端本地存储阿里云OSS存储上传Demo实现上传ConfigurationProperties 前端传出文件 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>上传文件</title> </head&g…

[人工智能] 结合最新技术:Transformer、CLIP与边缘计算在提高人脸识别准确率中的应用

随着人工智能的快速发展&#xff0c;特别是深度学习和自然语言处理领域的革命性技术&#xff0c;越来越多的前沿技术被应用于人脸识别中。Transformer架构、CLIP模型以及边缘计算的结合&#xff0c;正成为提升人脸识别准确率和应用效能的关键技术路径。特别是在多样化场景下&am…

Vue3 + ElementPlus动态合并数据相同的单元格(超级详细版)

最近的新项目有个需求需要合并单元列表。ElementPlus 的 Table 提供了合并行或列的方法&#xff0c;可以参考一下https://element-plus.org/zh-CN/component/table.html 但项目中&#xff0c;后台数据返回格式和指定合并是动态且没有规律的&#xff0c;Element 的示例过于简单&…

关于AI面试系统2025年趋势评估!

在快速发展的科技浪潮中&#xff0c;AI技术正以前所未有的速度渗透到各行各业。企业招聘领域&#xff0c;作为人才选拔的关键环节&#xff0c;也不例外地迎来了AI面试系统的广泛应用和持续创新。2025年&#xff0c;AI面试系统不仅成为企业招聘的主流工具&#xff0c;更在智能化…

MySQL 01 02 章——数据库概述与MySQL安装篇

一、数据库概述 &#xff08;1&#xff09;为什么要使用数据库 数据库可以实现持久化&#xff0c;什么是持久化&#xff1a;数据持久化意味着将内存中的数据保存到硬盘上加以“固化”持久化的主要作用是&#xff1a;将内存中的数据存储在关系型数据库中&#xff0c;当然也可以…

Vue3 组件

文章目录 Vue3 组件概述根组件定义和使用组件样式控制全局样式局部样式深度样式 Vue3 组件 概述 组件允许我们将 UI 划分为独立的、可重用的部分&#xff0c;并且可以对每个部分进行单独的思考。在实际应用中&#xff0c;组件常常被组织成层层嵌套的树状结构&#xff1a; 根…

怎么在家访问公司服务器?

在日常工作中&#xff0c;特别是对信息技术从业者而言&#xff0c;工作往往离不开公司的服务器。他们需要定期访问服务器&#xff0c;获取一些关键的机密文件或数据。如果您在家办公&#xff0c;并且需要处理未完成的任务&#xff0c;同时需要从公司服务器获取所需的数据&#…

IP-Guard对SolidWorks PDM 加密授权说明

SolidWorks PDM 加密授权说明 一、简介 该功能主要实现了,在SolidWorks Enterprise PDM环境下,可以正常"检入"和"检出" 加密文件,并可以正常预览加密文件。 二、操作说明 2.1 设置加密授权 安装客户端后,登录控制台,选择客户端计算机或组右键选择“…

AI 助力游戏开发中的常用算法实现

在当今的游戏开发领域&#xff0c;人工智能&#xff08;AI&#xff09;技术的应用已经成为推动行业发展的关键力量。AI不仅能够提升游戏的智能化水平&#xff0c;还能够增强玩家的沉浸感和游戏体验。随着技术的进步&#xff0c;AI在游戏设计、开发和测试中的应用越来越广泛&…

重现ORA-01555 细说Oracle Undo 数据管理

1. 概述 1.1. Undo 数据应用 undo数据是&#xff1a; 原始的、修改之前的数据副本 是针对更改数据的每个事务处理所捕获的 至少保留到事务处理结束 用于支持&#xff1a; 回退操作 读取一致性查询 闪回查询、闪回事务处理和闪回表 从失败的事务处理中进行恢复 1.2. 事…

Java 集合 Collection、List、Set

一. Collection 单列集合 1. Collection代表单列集合&#xff0c;每个元素(数据)只包含一个值 2. Collection集合特点 ① List系列集合&#xff1a;添加的元素是有序、可重复、有索引。 ArrayList、LinekdList&#xff1a;有序、可重复&#xff0c;有索引 ② Set系列集合&…

wamp php7.4 运行dm8

背景 1、电脑安装了dm8&#xff0c;具体参照官网dm8安装 2、安装好了wamp&#xff0c;我当前的php版本切换成了7.4的&#xff0c;我wamp的安装路径d:\wamp64\ 操作 3、查看phpinfo&#xff0c;如果Thread Safet为enabled&#xff0c;则选择pdo74_dm.dll&#xff0c;否则选择…

linux 系统 mysql :8.4.3 主从复制 教程及运维命令

一、环境准备 硬件配置CPU2 核 CPU内存2 GB 内存硬盘30 GB 硬盘容量外网访问服务器可以访问外网软件环境操作系统Anolis OS 7.9MySQL版本8.4.3 二、服务器清单 Master192.168.153.221Node192.168.153.222 三、安装mysql &#xff08;两台机器都要下载&#xff09; # 下载 …