【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
思路
我们将通过 二分查找 来解决这个问题,具体步骤如下:
- 确保 nums1 是较短的数组
- 由于我们要在较短的数组上进行二分查找,为了减少计算复杂度,我们首先确保 nums1 的长度小于或等于 nums2。如果 nums1 的长度大于 nums2,我们交换这两个数组。
- 这样做的目的是保证二分查找的次数最小化,最大化效率。
- 分割数组
- 我们的目标是将两个数组分割成左右两部分,使得合并后的两个部分的元素个数相同,或者左边多一个元素(如果总长度是奇数)。具体来说,假设 nums1 的长度是 n,nums2 的长度是 m,则:
- 左边部分的元素个数应为 (n + m + 1) / 2,这个值可以保证左边部分最多比右边部分多一个元素(当总长度是奇数时)。
- 右边部分的元素个数为 n + m - (n + m + 1) / 2。
- 二分查找
- 在 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)/2−partition1
这个公式确保了左边部分的总元素个数为 (n + m + 1) / 2。
- 确保分割符合条件
- 为了保证左边部分的所有元素都小于或等于右边部分的所有元素,我们需要检查:
- nums1[partition1 - 1] <= nums2[partition2](nums1 左边的最大值小于 nums2 右边的最小值)。
- nums2[partition2 - 1] <= nums1[partition1](nums2 左边的最大值小于 nums1 右边的最小值)。
如果这些条件成立,说明我们找到了合适的分割位置。
- 计算中位数
- 如果两个数组的总长度是奇数,中位数就是左边部分的最大值,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[partition1−1],nums2[partition2−1])+min(nums1[partition1],nums2[partition2]))/2
- 二分查找的调整
- 如果不满足条件,意味着 partition1 需要调整:
- 如果 nums1[partition1 - 1] > nums2[partition2],则需要将 partition1 向左移,即减小 partition1。
- 如果 nums2[partition2 - 1] > nums1[partition1],则需要将 partition1 向右移,即增大 partition1。
- 边界条件
- 对于数组的边界,我们使用 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;}
}相关文章:
【LeetCode Hot100 二分查找】搜索插入位置、搜索二维矩阵、搜索旋转排序数组、寻找两个正序数组的中位数
二分查找 搜索插入位置搜索二维矩阵在排序数组中查找元素的第一个和最后一个位置寻找旋转排序数组中的最小值搜索旋转排序数组寻找两个正序数组的中位数(hard) 搜索插入位置 给定一个排序数组和一个目标值,在数组中找到目标值,并…...
使用MediaPipe Face Mesh 面部动作检测
一、技术选型 OpenCV(Open Source Computer Vision Library) 用于视频流捕捉、图像预处理和基本图像处理操作。 MediaPipe 提供高效的人脸检测与关键点提取功能(Face Mesh)。 Python 作为后端开发语言,整合上述库进行…...
【Vue】<script setup>和 <script>区别是什么?在使用时的写法区别?
<script setup> 是 Vue 3 引入的一种新的脚本语法,它提供了一种更简洁和声明式的方式来编写组件逻辑。它是为了解决传统 <script> 标签在 Vue 单文件组件(SFC)中的一些局限性而设计的。 <script setup> 与 <script>…...
微服务框架,Http异步编程中,如何保证数据的最终一致性
一、背景 在微服务框架下,跨服务之间的调用,当遇到操作耗时或者量大的情况,我们一般会采用异步编程实现。 本文出现的问题是:异步回调过来时,却未查询到数据库中的任务,导致未能正常处理回调。 下面是当…...
vue3-dom-diff算法
vue3diff算法 什么是vue3diff算法 Vue3中的diff算法是一种用于比较虚拟DOM树之间差异的算法,其目的是为了高效地更新真实DOM,减少不必要的重渲染 主要过程 整个过程主要分为以下五步 前置预处理后置预处理仅处理新增仅处理后置处理包含新增、卸载、…...
年会抽奖Html
在这里插入图片描述 <!-- <video id"backgroundMusic" src"file:///D:/background.mp3" loop autoplay></video> --> <divstyle"width: 290px; height: 580px; margin-left: 20px; margin-top: 20px; background: url(D:/nianhu…...
ubuntu16 重启之后lvm信息丢失故障恢复
一、背景 1、问题背景 业务有一台物理开发服务器,文件系统有损坏;由于重启时没有检查,导致重启卡住。后面通过断电重新启动之后,无法进入系统;进入救援模式,注释数据盘挂载。重启之后进入系统,…...
【华为OD-E卷 - 热点网站统计 100分(python、java、c++、js、c)】
【华为OD-E卷 - 热点网站统计 100分(python、java、c、js、c)】 题目 企业路由器的统计页面,有一个功能需要动态统计公司访问最多的网页URL top N。请设计一个算法,可以高效动态统计Top N的页面 输入描述 每一行都是一个URL或…...
Ubuntu下安装Android Sdk
下载android sdk命令行工具 https://developer.android.com/studio?hlzh-cn#command-tools mkdir android-sdk cd android-sdk unzip commandlinetools-linux-11076708_latest.zip 添加环境变量到~/.bashrc export ANDROID_HOME$HOME/android-sdk export PATH$PATH:$ANDRO…...
【JVM】总结篇-类的加载篇之 类的加载器 和ClassLoader分析
文章目录 类的加载器ClassLoader自定义类加载器双亲委派机制概念源码分析优势劣势如何打破Tomcat 沙箱安全机制JDK9 双亲委派机制变化 类的加载器 获得当前类的ClassLoader clazz.getClassLoader() 获得当前线程上下文的ClassLoader Thread.currentThread().getContextClassLoa…...
怎样修改el-table主题样式
起因:el-table有主题样式,部分需要单独设置 环境:ideanodejs插件谷歌浏览器 第一步:找到scss文件: 谷歌浏览器打开表格页面,ctrlshifti打开开发者工具,点击后鼠标移动到表格单元格上单击一下…...
MySQL(二)MySQL DDL数据库定义语言
1. MySQL DDL数据库定义语言 1.1. MySQL定义语言 进入MySQL mysql -u root -p(回车后输入密码,即可进入mysq1)1.1.1. 数据库操作 (1)查看数据库 mysql>show databases;注:MySQL语句分隔符为“;” mysql库很重要它里面有…...
Spring Boot 项目启动报 NoClassDefFoundError 异常的原因分析与解决方案 - jackson 版本不一致
目录 报错: 问题分析: 解决方案: 方案 1:对 Jackson 版本进行统一 方案 2:升级 Springfox 版本 方案 3:替换 Springfox 为 springdoc-openapi(推荐) 方案 4:排除冲突的 Jack…...
原型与原型链
什么是原型(对象) 在JavaScript中,每个对象都具有一个原型对象prototype,目的是:利用原型对象实现在同一原型链中的原型方法共享 在理解原型对象前,需要先了解什么是构造函数 构造函数 用来初始化对象的…...
【Linux】信号处理
一、Linux系统信号 1、常见的系统信号 常见的Linux系统信号 信号值描述1SIGHUP挂起(hang up)进程2SIGINT中断进(interrupt)程3SIGQUIT停止(stop)进程9SIGKILL无条件终止(terminate)…...
5个不同类型的mysql数据库安装
各种社区版本下载官方地址:MySQL :: MySQL Community Downloads 一、在线YUM仓库(Linux) 选择 MySQL Yum Repository 选择对应版本下载仓库安装包(No thanks, just start my download.) 下载方法1:下载到本…...
python学习笔记—12—布尔类型、if语句
1. 布尔类型 (1) 定义 (2) 比较运算符 (3) 代码演示 1. 手动定义 bool_1 True bool_2 False print(f"bool_1的内容是:{bool_1}, 类型是:{type(bool_1)}") print(f"bool_2的内容是:{bool_2}, 类型是:{type(bool…...
分数阶傅里叶变换代码 MATLAB实现
function Faf myfrft(f, a) %分数阶傅里叶变换函数 %输入参数: %f:原始信号 %a:阶数 %输出结果: %原始信号的a阶傅里叶变换N length(f);%总采样点数 shft rem((0:N-1)fix(N/2),N)1;%此项等同于fftshift(1:N),起到翻…...
《数据结构》期末考试测试题【中】
《数据结构》期末考试测试题【中】 21.循环队列队空的判断条件为?22. 单链表的存储密度比1?23.单链表的那些操作的效率受链表长度的影响?24.顺序表中某元素的地址为?25.m叉树第K层的结点数为?26. 在双向循环链表某节点…...
openwrt 清缓存命令行
一、查看缓存 : free -m 二、清缓存:echo 3 > /proc/sys/vm/drop_caches 三、详解。 释放物理页缓存 echo 1 > /proc/sys/vm/drop_caches 释放可回收的slab对象,包含inode and dentry echo 2 > /proc/sys/vm/drop_caches 同时…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
