当前位置: 首页 > news >正文

LeetCode - 4. 寻找两个正序数组的中位数

. - 力扣(LeetCode)

题目

给定两个大小分别为 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.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -10^6 <= nums1[i], nums2[i] <= 10^6

解题方案

首先明确中位数的位置

1. 暴力解法(合并数组)

合并成一个新数组,然后sort,取中位数。

  • 边界情况:合并后数组的长度为0,则无中位数
  • 合并数组(nums)长度为偶数(n),则中位数为第\frac{n}{2}位和第\frac{n}{2}+1位的平均值,即mid=\frac{nums[\frac{n}{2}-1]+nums[\frac{n}{2}]}{2}
  • 合并数组(nums)长度为奇数(n), 则中位数为第\frac{n+1}{2}位,即nums[\frac{n-1}{2}]

接下来,人生苦短,我用Python~

class Solution:def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:# 合并数组并排序nums1.extend(nums2)nums1.sort()length = len(nums1)if length < 1:return Noneif length % 2 == 0:return (nums1[length // 2 - 1] + nums1[length // 2]) / 2else:return nums1[length // 2]

分析时空复杂度

  • 记nums1长度为m, nums2长度为n
  • 合并数组时间复杂度是O(m+n),空间复杂度是O(m+n)
  • 数组排序时间复杂度是O((n+m)log(n+m)),空间复杂度是O((n+m)log(n+m))

故总的时间复杂度是O((n+m)log(n+m)),空间复杂度是O((n+m)log(n+m))

虽然看上去代码非常精炼,但是从时空复杂度上看,算法并不好,毕竟题目中"正序(从小到大)数组"这个条件没有用到。因此考虑有序归并

2. 暴力解法优化版(有序合并数组)

class Solution:def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:m = len(nums1)n = len(nums2)# 合并数组nums = []if m == 0:nums = nums2elif n == 0:nums = nums1else:index_1 = 0index_2 = 0while True:if index_1 >= m and index_2 >= n:breakelif index_2 >= n or (index_1 < m and nums1[index_1] <= nums2[index_2]):nums.append(nums1[index_1])index_1 += 1elif index_1 >=m or (index_2 < n and nums1[index_1] > nums2[index_2]):nums.append(nums2[index_2])index_2 += 1total_length = len(nums)if total_length < 1:return Noneelif total_length % 2 == 0:return (nums[total_length // 2 - 1] + nums[total_length // 2]) / 2else:return nums[total_length // 2]

分析时空复杂度

  • 时间复杂度为合并数组花费的时间,O(m+n)
  • 空间复杂度为合并后数组的空间,O(m+n) (如果不存储合并的数组,空间复杂度可以做到O(1))

题目给出的条件都用上了,时空复杂度也得到了提升,但仍然不符合题目要求的 O(log (m+n))的时间复杂度,需要进一步优化。

有序数组寻找中位数,考虑二分法。

3. 二分法

考虑一个长度为n有序数组的中位数,其中位数:

  • 如果n为奇数,则中位数为第\frac{n+1}{2}大的数。
  • 如果n为偶数,则中位数为第\frac{n}{2}大的数和第\frac{n+1}{2}大的数的平均值。

因此,中位数问题可以转换为另一个问题,寻找数组中第k小的数

(对于长度为偶数的情况,需要寻找两次,并取平均值)

class Solution:def getKthNumber(self,  nums1: List[int], nums2: List[int], k: int) -> int:"""寻找两个正序整数数组中,第k小的数"""passdef findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:"""中位数计算入口函数"""total_length = len(num1) + len(nums2)if total_length < 1:return Noneelif total_length % 2 == 0:return (self.getKthNumber(nums1, nums2, total_length // 2) + self.getKthNumber(nums1, nums2, total_length // 2 + 1)) / 2else:return self.getKthNumber(num1, nums2, total_length // 2 + 1)

接着看如何寻找两个正序数组中第k小的数:

  • 如果时间复杂度是O(log (m+n)),考虑用二分法。

先看下面这个例子:

 

由此,我们可以总结我们两个正序数组中,寻找第k小的数的思路:

1. 如果k=1, 则首先比较两个数组首位元素,取最小的一个。

2. 如果k>1, 则比较两个数组中第\frac{k}{2}个元素,较小的一个数组中的第1~\frac{k}{2}个元素一定是小于我们目标值的,因此可以排除这一段(如上图中灰色部分),在剩余元素中继续寻找第(k-\frac{k}{2})小的元素。

3. 如果某个数组完全被排除掉了,则直接在剩余的另一个数组中定位目标元素即可。如下面的例子:

class Solution:def getKthNumber(self,  nums1: List[int], nums2: List[int], k: int) -> int:"""寻找两个正序整数数组中,第k大的数"""# 记录数组长度m = len(nums1)n = len(nums2)# 记录数组可以查找的起始位置索引index_1 = 0index_2 = 0while True:# 极端情况1 - nums1已经被排除为空if index_1 >= m:return nums2[index_2 + k - 1]# 极端情况2 - nums2已经被排除为空if index_2 >= n:return nums1[index_1 + k - 1]# 极端情况3 - 寻找最小的数if k == 1:return min(nums1[index_1], nums2[index_2])# 正常情况, 二分查找new_index_1 = min(k // 2 - 1 + index_1, m - 1)new_index_2 = min(k // 2 - 1 + index_2, n - 1)if nums1[new_index_1] <= nums2[new_index_2]:k -= (new_index_1 - index_1 + 1)index_1 = new_index_1 + 1else:k -= (new_index_2 - index_2 + 1)index_2 = new_index_2 + 1def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:"""寻找中位数 入口函数"""total_length = len(nums1) + len(nums2)if total_length < 1:return Noneelif total_length % 2 == 0:return (self.getKthNumber(nums1, nums2, total_length // 2) + self.getKthNumber(nums1, nums2, total_length // 2 + 1)) / 2else:return self.getKthNumber(nums1, nums2, total_length // 2 + 1)

分析时空复杂度

  • 时间复杂度O(log(m+n))

  • 空间复杂度O(1) 

AI会怎么做?

智谱清言给出了一种更高效的解法,划分数组二分法。

思路:

  • 对一个长度为n的数组,从任意位置i将数组划分为两部分,可以有n+1种分发(i \in [0, n]),如图所示,4个元素的数组,有4+1=5种划分方法。

  • 中位数的作用是什么呢?将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。
    • 对数组A从i位置进行划分,对数组B在j位置划分,数组A的左半部分和数组B的左半部分合起来叫做left_part, 数组A的有半部分和数组B的右半部分合起来叫做right_part, 则中位数是这样的一种划分:
      • 对于两个数组长度之和为偶数的情况:\left\{\begin{matrix} {len(left\_part) == len(right\_part) }\\{max(left\_part) <= min(right\_part)} \end{matrix}\right.,中位数为mid=\frac{max(left\_part)+min(right\_part)}{2},此时划分位置满足i + j = m-i+n-j\quad i\in[0,m],j\in[0,n]
      • 对于两个数组长度之和为奇数的情况:\left\{\begin{matrix} {len(left\_part) == len(right\_part) + 1}\\{max(left\_part) <= min(right\_part)} \end{matrix}\right.,中位数为mid = max(left\_part),此时划分位置满足i+j=m-i + n-j +1\quad i\in[0,m],j\in[0,n]
      • 合并上述两种,可以知道中位数情况下的划分:i+j =int(\frac{m+n+1}{2}) \qquad i\in[0,m],j\in[0,n]
      • 如果我们保证m <=n(避免j计算出负数), 可以导出:j=int(\frac{m+n+1}{2}) - i \qquad j\in[0,n]
      • 通过上述推导,中位数问题可以转换为:寻找i\in[0,m],使得:B[j-1] \leqslant A[i]A[i-1]\leqslant B[j],其中j=int(\frac{m+n+1}{2}) - i
      • 进一步简化为:[0,m]中寻找最大的i,使得A[i-1]\leqslant B[j],其中j=int(\frac{m+n+1}{2}) - i。接下来就可以在[0,m]上对i进行二分查找了。

如此复杂的推导过程,又是担心被AI取代的一天。。。

def findMedianSortedArrays(nums1, nums2):# 确保 nums1 是较短的数组if len(nums1) > len(nums2):nums1, nums2 = nums2, nums1m, n = len(nums1), len(nums2)imin, imax, half_len = 0, m, (m + n + 1) // 2while imin <= imax:i = (imin + imax) // 2j = half_len - iif i < m and nums1[i] < nums2[j - 1]:# i 需要增大imin = i + 1elif i > 0 and nums1[i - 1] > nums2[j]:# i 需要减小imax = i - 1else:# 找到合适的 i 和 jmax_of_left = 0if i == 0: max_of_left = nums2[j - 1]elif j == 0: max_of_left = nums1[i - 1]else: max_of_left = max(nums1[i - 1], nums2[j - 1])if (m + n) % 2 == 1:return max_of_leftmin_of_right = 0if i == m: min_of_right = nums2[j]elif j == n: min_of_right = nums1[i]else: min_of_right = min(nums1[i], nums2[j])return (max_of_left + min_of_right) / 2.0# 示例
print(findMedianSortedArrays([1, 3], [2]))  # 输出: 2.0
print(findMedianSortedArrays([1, 2], [3, 4]))  # 输出: 2.5

相关文章:

LeetCode - 4. 寻找两个正序数组的中位数

. - 力扣&#xff08;LeetCode&#xff09; 题目 给定两个大小分别为 m 和 n 的正序&#xff08;从小到大&#xff09;数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (mn)) 。 示例 1&#xff1a; 输入&#xff1a;nums1 …...

算法设计与分析——动态规划

1.动态规划基础 1.1动态规划的基本思想 动态规划建立在最优原则的基础上&#xff0c;在每一步决策上列出可能的局部解&#xff0c;按某些条件舍弃不能得到最优解的局部解&#xff0c;通过逐层筛选减少计算量。每一步都经过筛选&#xff0c;以每一步的最优性来保证全局的最优性…...

【实战篇】GEO是什么?还可以定义新的数据类型吗?

背景 之前&#xff0c;我们学习了 Redis 的 5 大基本数据类型&#xff1a;String、List、Hash、Set 和 Sorted Set&#xff0c;它们可以满足大多数的数据存储需求&#xff0c;但是在面对海量数据统计时&#xff0c;它们的内存开销很大&#xff0c;而且对于一些特殊的场景&…...

SpringBoot最佳实践之 - 项目中统一记录正常和异常日志

1. 前言 此篇博客是本人在实际项目开发工作中的一些总结和感悟。是在特定需求背景下&#xff0c;针对项目中统一记录日志(包括正常和错误日志)需求的实现方式之一&#xff0c;并不是普适的记录日志的解决方案。所以阅读本篇博客的朋友&#xff0c;可以参考此篇博客中记录日志的…...

【Flutter】状态管理:高级状态管理 (Riverpod, BLoC)

当项目变得更加复杂时&#xff0c;简单的状态管理方式&#xff08;如 setState() 或 Provider&#xff09;可能不足以有效地处理应用中状态的变化和业务逻辑的管理。在这种情况下&#xff0c;高级状态管理框架&#xff0c;如 Riverpod 和 BLoC&#xff0c;可以提供更强大的工具…...

OAK相机的RGB-D彩色相机去畸变做对齐

▌低畸变标准镜头的OAK相机RGB-D对齐的方法 OAK相机内置的RGB-D管道会自动将深度图和RGB图对齐。其思想是将深度图像中的每个像素与彩色图像中对应的相应像素对齐。产生的RGB-D图像可以用于OAK内置的图像识别模型将识别到的2D物体自动映射到三维空间中去&#xff0c;或者产生的…...

smartctl硬盘检查工具

一、smartctl工具简介   Smartmontools是一种硬盘检测工具&#xff0c;通过控制和管理硬盘的SMART(Self Monitoring Analysis and Reporting Technology)&#xff0c;自动检测分析及报告技术)技术来实现的&#xff0c;SMART技术可以对硬盘的磁头单元、盘片电机驱动系统、硬盘…...

清空MySQL数据表

要清空 MySQL 数据表&#xff0c;您可以使用 TRUNCATE 或 DELETE 命令 使用 TRUNCATE 命令 TRUNCATE 命令用于删除表中的所有数据&#xff0c;并重置自增 ID&#xff08;如果存在&#xff09;&#xff1a; TRUNCATE TABLE table_name;将 table_name 替换为您要清空的表的名称…...

2024年妈杯MathorCup大数据竞赛A题超详细解题思路

2024年妈杯大数据竞赛初赛整体难度约为0.6个国赛。A题为台风中心路径相关问题&#xff0c;为评价预测问题&#xff1b;B题为库存和销量的预测优化问题。B题难度稍大于A题&#xff0c;可以根据自己队伍情况进行选择。26日早六点之前发布AB两题相关解题代码论文。 下面为大家带来…...

Kafka系列之:Kafka集群磁盘条带划分和Kafka集群磁盘扩容详细方案

Kafka系列之:Kafka集群磁盘条带划分和Kafka集群磁盘扩容详细方案 一、lsblk命令二、Kafka节点磁盘条带化方案一三、Kafka节点磁盘条带化方案二四、理解逻辑区块LE五、查看kafka节点磁盘条带划分情况六、Kafka节点磁盘扩容一、lsblk命令 lsblk命令用于列出块设备的信息,包括磁…...

【LeetCode】修炼之路-0007- Reverse Integer (整数反转)【python】

题目 Reverse Integer Given a signed 32-bit integer x, return x with its digits reversed. If reversing x causes the value to go outside the signed 32-bit integer range [-231, 231 - 1], then return 0. Assume the environment does not allow you to store 64-b…...

【Flutter】页面布局:线性布局(Row 和 Column)

在 Flutter 中&#xff0c;布局&#xff08;Layout&#xff09;是应用开发的核心之一。通过布局组件&#xff0c;开发者可以定义应用中的控件如何在屏幕上排列。Row 和 Column 是 Flutter 中最常用的两种线性布局方式&#xff0c;用于水平和垂直排列子组件。在本教程中&#xf…...

C语言巨难题:执行操作可获得的最大总奖励 I(C语言版)

1.题目&#xff1a; 给你一个整数数组 rewardValues&#xff0c;长度为 n&#xff0c;代表奖励的值。 最初&#xff0c;你的总奖励 x 为 0&#xff0c;所有下标都是 未标记 的。你可以执行以下操作 任意次 &#xff1a; 从区间 [0, n - 1] 中选择一个 未标记 的下标 i。如果…...

【力扣】GO解决子序列相关问题

文章目录 一、引言二、动态规划方法论深度提炼子序列问题的通用解法模式 三、通用方法论应用示例&#xff1a;最长递增子序列&#xff08;LeetCode题目300&#xff09;Go 语言代码实现 四、最长连续递增序列&#xff08;LeetCode题目674&#xff09;Go 语言代码实现 五、最长重…...

Ubuntu20.04安装VM tools并实现主机和虚拟机之间文件夹共享

1、Ubuntu20.04安装VM tools 参考这个&#xff0c;很详细&#xff1a;Ubuntu 20.04 安装 VMwareTools 教程 2、实现主机与VMware虚拟机共享文件夹 设置共享文件夹参考&#xff1a;windows和虚拟机互传文件的三种方式 挂载操作参考&#xff1a;主机与VMware虚拟机共享文件夹&…...

Linux 学习笔记(十七)—— 文件系统

终极目标&#xff1a;理解 inode 和 软硬连接&#xff1b; 文件系统&#xff1a;Ext2; 文件 文件内容 文件属性; ——> 磁盘上存储的文件 存储的文件内容 存储的文件属性&#xff1b; Linux系统中&#xff1a;文件内容使用数据块存储&#xff0c;文件属性使用inode(固定…...

【计算机网络 - 基础问题】每日 3 题(五十八)

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞…...

Netty入门基础:IO模型中BIO\NIO概念及区别【附演示代码】

文章目录 &#x1f600;BIO&#x1f4a2;实战demo &#x1f308;NIO&#x1f3cd;Buffer核心属性核心方法 &#x1f397;Channel&#x1f388;Selector核心方法 &#x1f9e8;实战demo &#x1f3a8;粘包与半包 &#x1f600;BIO 传统IO模型&#xff0c;同步阻塞&#xff0c;每…...

vue2 使用环境变量

一. 在根目录下创建.env.xxx文件 .env 基础系统变量&#xff0c;无论何种环境&#xff0c;都可使用其中配置的值&#xff0c;其他环境中的变量会覆盖.env中的同名变量。 .env.development 开发环境 .env.production 生产环境 .env.staging 测试环境 二. 内容格式 vue2 使用是以…...

数据预处理

继续提取代码片段&#xff1a; 12. **导入iris数据集并查看前5行数据**&#xff1a; python from sklearn.datasets import load_iris iris load_iris() X iris.data print(iris数据集的维度为:, X.shape) print(iris数据集的前5行数据为:\n, X[:5]) …...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

stm32wle5 lpuart DMA数据不接收

配置波特率9600时&#xff0c;需要使用外部低速晶振...