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

题目描述

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数

算法的时间复杂度应该为 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
  • -106 <= nums1[i], nums2[i] <= 106

解法

  1. 遍历解法

思路:

  • 先找到中位数在哪
  • 依次遍历两个数组,依次从两个数组前面取元素(都是数组最小的元素),比较大小,取较小的,
  • 更新元素小的那个数组的索引,向后移动一位,继续遍历比较
  • 只需要遍历到中位数次数即可。

java代码:

class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {// 先找到计算中位数的位置:如果是奇数则去中间的,否则取中间两个的平均数ArrayList<Integer> arr = new ArrayList<>();if ((nums1.length + nums2.length) % 2 == 1) {arr.add((nums1.length + nums2.length) / 2 + 1);} else {arr.add((nums1.length + nums2.length) / 2);arr.add((nums1.length + nums2.length) / 2 + 1);}// 中位数的和int sum = 0;int index1 = 0;  // 数组1的索引int index2 = 0;  // 数组2的索引int count = 0;   // 已经排序的总数// 遍历两个数组,依次从两个数组取元素,比较大小,取较小的while (count < (nums1.length + nums2.length) / 2 + 1) {// 比较两个数组的元素,更新元素小的那个数组的索引,并取出小的元素int min;if (index1 >= nums1.length) {// 说明nums1数组被取完了,去取nums2数组min = nums2[index2];index2 ++;} else if (index2 >= nums2.length) {// 说明nums2数组被取完了,去取nums1数组min = nums1[index1];index1 ++;} else if (nums1[index1] < nums2[index2]) {// nums1数组的元素更小min = nums1[index1];index1 ++;} else {// nums2数组的元素更小min = nums2[index2];index2 ++;}count ++;if (arr.contains(count)) {// 计算平均数的和sum += min;}}return (double)sum / arr.size();}
}

复杂度

时间复杂度:O(m+n),m和n分别为数组长度
空间复杂度:O(1)

  1. 二分 + 递归

思路:

  • 找第k大的数,那么每次先去排除k/2个数;
  • 分别两个数组里找第k/2的数,如果 k 是奇数,向下取整,然后比较两个数组的第k/2的数大小,小的那个数组的第k/2的数前面的数,肯定不是第K大的数;
  • 把小的数组的前面的数排除掉(k/2个),因为前面已经排除了k/2个数,所有后面只在两个数组里找第 k- k/2 大的数即可

注意特殊情况(递归终止条件):

  1. 每次都是取 k/2 的数进行比较,有时候可能会遇到数组长度小于 k/2的时候,那么只要取末尾那个数就行
  2. 如果有数组为空,则只取不为空的数组中位数

java代码:

class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {int len1 = nums1.length;int len2 = nums2.length;int sumLen = len1 + len2;if (sumLen % 2 == 1) {  // 奇数个return getKth(nums1, nums2, 0, len1 - 1, 0, len2 - 1, sumLen / 2 + 1);} else {  // 偶数个int mid1 = getKth(nums1, nums2, 0, len1 - 1, 0, len2 - 1, sumLen / 2);int mid2 = getKth(nums1, nums2, 0, len1 - 1, 0, len2 - 1, sumLen / 2 + 1);return (mid1 + mid2) / 2.0d;}// 时间复杂度是O(log(k)),因为k = (m + n)/2 ,所以时间复杂度O(log(m + n))}/*** 辅助函数:找两个数组第k大的数* 二分 + 递归* 整体思路:找第k大的数,那么每次先去排除k/2个数*          分别两个数组里找第k/2的数,如果 k 是奇数,向下取整,然后比较两个数组的第k/2的数大小,小的那个数组的第k/2的数前面的数,肯定不是第K大的数*          把小的数组的前面的数排除掉(k/2个),因为前面已经排除了k/2个数,所有后面只在两个数组里找第 k- k/2 大的数即可*          特殊情况:1. 每次都是取 k/2 的数进行比较,有时候可能会遇到数组长度小于 k/2的时候,那么只要取末尾那个数就行*                   2.如果有数组为空,则只取不为空的数组中位数** @param nums1  数组1* @param nums2  数组2* @param start1 数组1的起始索引* @param end1  数组1的结束索引* @param start2  数组2的起始索引* @param end2  数组2的结束索引* @param k* @return*/private static int getKth(int[] nums1, int[] nums2, int start1, int end1, int start2, int end2, int k) {int len1 = end1 - start1 + 1;int len2 = end2 - start2 + 1;// 让 len1 的长度小于 len2,这样就能保证如果有数组空了,一定是 len1if (len1 > len2) {return getKth(nums2, nums1, start2, end2, start1, end1, k);}// 下面是递归的两个结束条件// 1. 如果第一个数组长度为0,直接找第二个数组中位数即可if (len1 == 0) {return nums2[start2 + k -1];}// 2. 如果k等于1,直接找两个数组中第一个元素小的那个元素if (k == 1) {return Math.min(nums1[start1], nums2[start2]);}// 找两个个数组的第k/2个索引// 小心这里可能数组越界,即第一个数组的长度没有第k/2个数大了,那么就取最后一个int index1 = start1 + Math.min(len1, k/2) -1;int index2 = start2 + Math.min(len2, k/2) -1;// 比较两个数组索引位置的元素大小,小的那个前面的元素不要了,继续递归调用找第k- k/2 大的数// 注意点:这里的第k- k/2 大的,需要考虑到上面可能第一个数组的长度没有第k/2个数大了// 那么就只排除了第一个数组长度的数,所以,不应该找第k- k/2 大的数,而是找第k- num1.length 大的数,// 两种情况综合考虑,即找第 k - Math.min(len2, k/2)大的数,可用k - (index1 - start1 + 1)表示if (nums1[index1] < nums2[index2]) {return getKth(nums1, nums2, index1 + 1, end1, start2, end2, k - (index1 - start1 + 1));} else {return getKth(nums1, nums2, start1, end1, index2 + 1, end2, k - (index2 - start2 + 1));}}
}

复杂度

时间复杂度:O(log(m + n)),时间复杂度是O(log(k)),因为k = (m + n)/2 ,所以时间复杂度O(log(m + n))
空间复杂度:O(1)

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

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

相关文章

MediaPipe - 小记

文章目录 关于 MediaPipe 关于 MediaPipe Cross-platform, customizable ML solutions for live and streaming media. MediaPipe &#xff1a; 设备端机器学习框架 MediaPipe Studio : 提供标准化 task&#xff0c;你可以下载预训练模型来使用 MediaPipe Model Maker : 定制…

python中的字符串

字符串 字符串是编程语言中的一种基本数据类型&#xff0c;用于表示一串字符序列。在Python中&#xff0c;字符串是不可变的&#xff0c;也就是说一旦字符串被创建&#xff0c;就无法修改其中的字符。 Python中的字符串可以用单引号或双引号括起来&#xff0c;例如&#xff1…

构建SQL Server链接服务器:实现跨服务器数据访问及整合

点击上方蓝字关注我 在SQL Server数据库管理中&#xff0c;链接服务器是一项强大的功能&#xff0c;允许在一个SQL Server实例中访问另一个SQL Server实例的数据。这种功能为数据库管理员提供了灵活性&#xff0c;使其能够跨不同服务器进行数据交互&#xff0c;开辟了更多的应用…

RLHF:强化学习结合大预言模型的训练方式

RLHF (Reinforcement Learning from Human Feedback) 以强化学习方式依据人类反馈优化语言模型。 文章目录 一、简介二、一般的流程三、微调gpt介绍示例 参考文章 一、简介 强化学习从人类反馈中学习&#xff08;RLHF&#xff0c;Reinforcement Learning from Human Feedback&a…

【代码随想录】算法训练计划37

贪心 1、738. 单调递增的数字 题目&#xff1a; 输入: n 10 输出: 9 思路&#xff1a; func monotoneIncreasingDigits(n int) int {// 贪心&#xff0c;利用字符数组s : strconv.Itoa(n)ss : []byte(s)leng : len(ss)if leng < 1 {return n}for i:leng-1; i>0; i-- …

为什么不把所有的函数都定义成内联函数

内联是以代码膨胀为代价&#xff0c;仅仅省去了函数调用的开销&#xff0c;从而提高函数的执行效率。如果执行函数体内代码的时间相比于函数调用的开销较大&#xff0c;那么效率的收获会很少。另一方面&#xff0c;每一处内联函数的调用都要复制代码&#xff0c;将使程序的总代…

vscode集成git

1、首先电脑要安装git 打开git官网地址&#xff1a;Git进行下载&#xff0c;如下图界面&#xff1a; 如图片中描述&#xff1a;一般进入官网后会识别电脑对应系统&#xff08;识别出了我的电脑是Windows系统 。如果未识别到电脑系统&#xff0c;可在左侧选择自己电脑对应的系统…

主成分分析例题 (多元统计分析期末复习)

例一 给定X的协差阵&#xff0c;对其进行主成分分析, &#xff08;1&#xff09;求出每个主成分的贡献率&#xff1b; &#xff08;2&#xff09;求出每个原始变量的信息提取率&#xff1b; 解&#xff1a;对于主成分分析的题&#xff0c;一般来说&#xff0c;题目给定一个协方…

us提升到ns级精度settimeofday()到clock_settime()

精度 clock_settime()精度比settimeofday()高&#xff0c;受限制于参数的结构体&#xff0c;一个是ns级精度&#xff0c;另一个us级精度。具体两个函数原因参考如下。 int clock_settime(clockid_t clk_id, const struct timespec *tp); int settimeofday(const struct timeva…

[vue3] 使用 vite 创建vue3项目的详细流程

一、vite介绍 Vite&#xff08;法语意为 “快速的”&#xff0c;发音 /vit/&#xff0c;发音同 “veet”) 是一种新型前端构建工具&#xff0c;能够显著提升前端开发体验&#xff08;热更新、打包构建速度更快&#xff09;。 二、使用vite构建项目 【学习指南】学习新技能最…

Elasticsearch:使用 ILM 示例运行降采样 (downsampling)

如果你对降采样还不是很熟的话&#xff0c;请阅读之前的文章 “Elasticsearch&#xff1a;对时间序列数据流进行降采样&#xff08;downsampling)”。这是一个简化的示例&#xff0c;可让你快速了解降采样如何作为 ILM 策略的一部分来减少一组采样指标的存储大小。 该示例使用典…

2021年3月9日 Go生态洞察:Go开发者调研结果解读

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

java对象 转换成json字符串 工具类 jackson

jackson概述 Jackson 是一个用于处理 JSON 数据的 Java 库&#xff0c;由 FasterXML 公司开发和维护。它提供了一组功能强大的 API&#xff0c;用于在 Java 对象和 JSON 数据之间进行高效的序列化&#xff08;将对象转换为 JSON 格式&#xff09;和反序列化&#xff08;将 JSO…

获取焦点后,样式异常的处理方法

问题 在使用monaco-editor 设置代码提示未正常显示&#xff0c;提示框出现&#xff0c;看不到内容&#xff0c;如图 看不到内容&#xff0c;有两种情况&#xff1a; 情况一&#xff1a;没有得到数据&#xff0c;所以没有展示&#xff1b; 情况二&#xff1a;得到了数据&#x…

数据预处理:随机裁剪放缩

随机裁剪放缩是一种数据增强技术&#xff0c;可以在训练神经网络时增加数据的多样性&#xff0c;提高模型的泛化能力。具体来说&#xff0c;随机裁剪放缩可以通过随机裁剪和缩放原始图片来生成多个不同的训练样本&#xff0c;从而增加数据集的大小和多样性。这种技术在图像分类…

CentOS一键安装docker脚本

CentOS安装Docker一键脚本 在CentOS上安装Docker是许多项目中常见的任务之一。为了简化这个过程&#xff0c;你可以使用下面的一键脚本。 #!/bin/bash# 卸载旧版本&#xff08;如果有&#xff09; sudo yum remove -y docker \docker-client \docker-client-latest \docker-c…

Django之Auth认证模块

文章目录 一、简介二、Auth模块是什么三、Auth模块常用方法create_user() 创建普通用户authenticate() 用户认证auth.login(HttpResponse&#xff0c;user)登录状态保持is_authenticated 登录认证判断auth.loginout(reqeust)退出登录login_required() 登录认证装饰器check_pass…

MySQL之JDBC

&#x1f495;"我像离家的孤儿,回到了母亲的怀抱,恢复了青春。"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;MySQL之JDBC 一.什么是JDBC? JDBC编程就是通过Java 代码来操纵数据库 数据库编程&#xff0c; 需要数据库服务器提供一些API供程序…

reversed函数(python)

在Python中&#xff0c;reversed()是一个内置函数&#xff0c;用于将序列&#xff08;如字符串、列表、元组等&#xff09;进行反转。它返回一个反向迭代器对象&#xff0c;可以使用list()函数将其转换为一个列表。 语法&#xff1a; reversed(sequence)参数&#xff1a; se…

原来10张图就可以搞懂分布式链路追踪系统原理

分布式系统为什么需要链路追踪&#xff1f; 随着互联网业务快速扩展&#xff0c;软件架构也日益变得复杂&#xff0c;为了适应海量用户高并发请求&#xff0c;系统中越来越多的组件开始走向分布式化&#xff0c;如单体架构拆分为微服务、服务内缓存变为分布式缓存、服务组件通…