Leetcode算法系列| 4. 寻找两个正序数组的中位数

news2024/10/3 10:44:07

目录

  • 1.题目
  • 2.题解
    • C# 解法一:合并List根据长度找中位数
    • C# 解法二:归并排序后根据长度找中位数
    • C# 解法三:方法二的优化,不真实添加到list
    • C# 解法四:第k小数
    • C# 解法五:从中位数的概念定义入手

1.题目

给定两个大小分别为 m 和 n 的正序(从小到大)数组 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
    • snums2.length == n
    • 0 <= m <= 1000
    • 0 <= n <= 1000
    • 1 <= m + n <= 2000
    • -10^6 <= nums1[i], nums2[i] <= 10^6

2.题解

C# 解法一:合并List根据长度找中位数

  • 提new 一个 List , 并将 nums1 和 nums2 都添加到list 中,然后进行排序。对于排序后的 list, 根据长度计算出中位数的index,进而计算出最终结果。假设合并后的list长度为13,则从小到大第7个数字为中位数,resultIndex=6;假设合并后的list长度为14,则从小到大第7,8个数字的平均值为中位数,index 分别为 6,7,此时resultIndex =7,resultIndex-1 =6 , 结果为 ( list[resultIndex-1] + list[resultIndex] ) / 2.0 ;
public class Solution {
     public double FindMedianSortedArrays(int[] nums1, int[] nums2)
    {
        int m = nums1.Length;
        int n = nums2.Length;
        int len = m + n;
        var resultIndex = len / 2;
        List<int> list = new List<int>(nums1);
        list.AddRange(nums2);
        list.Sort();
        if (len % 2 == 0)
        {
            return (list[resultIndex - 1] + list[resultIndex]) / 2.0;
        }
        else
        {
            return list[resultIndex];
        }
    }
}

1

  • 时间复杂度:O( (m+n)(1+log(m+n) ))
    • 将长度为m,n的两个数组添加到list,复杂度分别为常数级的m和n ;list.Sort()的复杂度根据官方文档可得为 (m+n)log(m+n),所以该方法时间复杂度为 O( m+n+(m+n)log(m+n) ) = O( (m+n)(1+log(m+n) ))
  • 空间复杂度:O(m+n)
    • 使用list的长度为m+n.

C# 解法二:归并排序后根据长度找中位数

  • 方法一使用了list.Sort() 方法,可以对list进行排序,但是,若题目给出的nums1 和 nums2 是无序数组,使用 list.Sort() 才算是 物有所用。 本题中 nums1 和 nums2 是有序数组,所以使用 list.Sort() 有写 杀鸡用宰牛刀的感觉,换句话说,这里面存在着效率的浪费。我们可以利用 【nums1 和 nums2 是有序数组】 这个条件,来精简我们的排序。
public class Solution {
    public double FindMedianSortedArrays(int[] nums1, int[] nums2)
    {
        // nums1 与 nums2 有序添加到list中
        List<int> list = new List<int>();
        int i = 0, j = 0;
        int m = nums1.Length;
        int n = nums2.Length;
        int len = m + n;
        var resultIndex = len / 2;

        while (i < m && j < n)
        {
            if (nums1[i] < nums2[j])
                list.Add(nums1[i++]);
            else
                list.Add(nums2[j++]);
        }
        while (i < m) list.Add(nums1[i++]);
        while (j < n) list.Add(nums2[j++]);

        if (len % 2 == 0)
        {
            return (list[resultIndex - 1] + list[resultIndex]) / 2.0;
        }
        else
        {
            return list[resultIndex];
        }
    }
}

2

  • 时间复杂度:O(m+n)
    • i 和 j 一起把长度为 m 和 n 的两个数组遍历了一遍,所以时间复杂度为 O(m+n)
  • 空间复杂度:O(m+n)
    • 使用list的长度为m+n.

C# 解法三:方法二的优化,不真实添加到list

  • 对于方法二,我们在已知 resultIndex 的情况下,也可以不把 nums1 和 nums2 真实添加到 list 中,只需要在i 和 j 不断向右移动的过程中,计算是否到达了 resultIndex 即可。 若到达了 resultIndex,可以直接返回结果,而不必再处理后面的数据。但是相对的,我们需要在 i 或者 j 向右移动时,判断是否到达了resultIndex.
public class Solution {
    public double FindMedianSortedArrays(int[] nums1, int[] nums2)
    {
        int i = 0, j = 0, m = nums1.Length, n = nums2.Length;
        int len = m + n;
        int resultIndex = len / 2;
        int resultIndexPre = resultIndex - 1;
        int result = 0, resultPre = 0;  
        bool isTwoResult = len % 2 == 0;
        while (i < m || j < n)
        {
            var nums1ii = i < m ? nums1[i] : int.MaxValue;
            var nums2jj = j < n ? nums2[j] : int.MaxValue;
            if (nums1ii < nums2jj)
            {
                if (i + j == resultIndexPre) resultPre = nums1[i];
                if (i + j == resultIndex)
                {
                    result = nums1[i];
                    if (isTwoResult) return (resultPre + result) / 2.0;
                    else return result;
                }
                i++;
            }
            else
            {
                if (i + j == resultIndexPre) resultPre = nums2[j];
                if (i + j == resultIndex)
                {
                    result = nums2[j];
                    if (isTwoResult) return (resultPre + result) / 2.0;
                    else return result;
                }
                j++;
            }
        }
        return 0;
    }
}

在这里插入图片描述

  • 时间复杂度:O(m+n)
    • i 和 j 一起把长度为 m 和 n 的两个数组遍历了一半,但是每一步都需要判断当前i+j的值是否等于resultIndex,所以时间复杂度仍可认为 O(m+n)
  • 空间复杂度:O(1)
    • 对比方法二,不再使用list,只使用了几个变量来存值,所以空间复杂度为O(1)

C# 解法四:第k小数

  • 前面的几种方法,时间复杂度都没有达到题目要求的 O(log(m+n)) 。 看到log,很明显需要使用二分法。根据 windliang提供的思路,题目求中位数,实际上是求第 k 小数的一种特殊情况,而求第 k 小数 有一种算法。

方法三中,i 和 j 每次向右移动一位时,相当于去掉了一个不可能是中位数的值,也就是一个一个的排除。由于给定的两个数组是有序的,所以我们完全可以一半一半的排除。假设我们要找第 k 小数,我们每次循环可以安全的排除掉 k/2 个数。

public class Solution {
    public double FindMedianSortedArrays(int[] nums1, int[] nums2)
    {
        int n = nums1.Length;
        int m = nums2.Length;
        int len = n + m;
        int kPre = (len + 1) / 2;
        int k = (len + 2) / 2;
        if (len % 2 == 0)
            return (GetKth(nums1, 0, n - 1, nums2, 0, m - 1, kPre) + GetKth(nums1, 0, n - 1, nums2, 0, m - 1, k)) * 0.5;
        else
            return GetKth(nums1, 0, n - 1, nums2, 0, m - 1, k);
    }

    private int GetKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k)
    {
        int len1 = end1 - start1 + 1;
        int len2 = end2 - start2 + 1;
        //让 len1 的长度小于 len2,这样就能保证如果有数组空了,一定是 len1 
        if (len1 > len2) return GetKth(nums2, start2, end2, nums1, start1, end1, k);
        if (len1 == 0) return nums2[start2 + k - 1];
        if (k == 1) return Math.Min(nums1[start1], nums2[start2]);
        int i = start1 + Math.Min(len1, k / 2) - 1;
        int j = start2 + Math.Min(len2, k / 2) - 1;
        if (nums1[i] > nums2[j])
            return GetKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
        else
            return GetKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1));
    }
}

1

  • 时间复杂度:O(log(m+n))
    • i每进行依次循环,就减少 k/2个元素,所以时间复杂度为 O(log(k)) , 而 k = (m+n)/2 , 所以最终复杂度是 O(log(m+n))
  • 空间复杂度:O(1)
    • 只使用了几个变量来存值,递归是尾递归不占用堆栈, 所以空间复杂度为O(1)

C# 解法五:从中位数的概念定义入手

  • 该方法参考了 LeetCode 题解的 官方题解 以及 windliang 的题解。
    首先我们来看一下百度百科中位数的定义:https://baike.baidu.com/item/%E4%B8%AD%E4%BD%8D%E6%95%B0/3087401?fr=aladdin
public class Solution {
  public double FindMedianSortedArrays(int[] A, int[] B)
    {
        int m = A.Length;
        int n = B.Length;
        //保证第一个数组是较短的
        if (m > n) return FindMedianSortedArrays(B, A);
        //正在寻找的范围为 [ A[iMin],A[iMax] ) , 左闭右开。二分查找取i=(iMin+iMax)/2
        int iMin = 0, iMax = m;
        while (iMin <= iMax)
        {
            int i = (iMin + iMax) / 2;
            int j = (m + n + 1) / 2 - i;
            if (j != 0 && i != m && B[j - 1] > A[i])
            { // i 需要增大
                iMin = i + 1;
            }
            else if (i != 0 && j != n && A[i - 1] > B[j])
            { // i 需要减小
                iMax = i - 1;
            }
            else
            { // 达到要求,并且将边界条件列出来单独考虑
                int maxLeft = 0;
                if (i == 0) { maxLeft = B[j - 1]; }
                else if (j == 0) { maxLeft = A[i - 1]; }
                else { maxLeft = Math.Max(A[i - 1], B[j - 1]); }
                if ((m + n) % 2 == 1) { return maxLeft; } // 奇数的话不需要考虑右半部分

                int minRight = 0;
                if (i == m) { minRight = B[j]; }
                else if (j == n) { minRight = A[i]; }
                else { minRight = Math.Min(B[j], A[i]); }

                return (maxLeft + minRight) / 2.0; //如果是偶数的话返回结果
            }
        }
        return 0.0;
    }
}

5

  • 时间复杂度:O(log(min(m,n))
    • 我们对较短的数组进行了二分查找,所以时间复杂度是 O(log(min(m,n))
  • 空间复杂度:O(1)
    • 只使用了几个变量来存值,所以空间复杂度为O(1)

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

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

相关文章

大模型工具_QUIVR

https://github.com/StanGirard/quivr/ 24.5K Star 1 功能 整体功能&#xff0c;想解决什么问题 实现了前后端结合的 RAG 方案。构建能直接使用的应用。提出了“第二大脑”&#xff0c;具体实现也是RAG&#xff0c;但针对不同用户不同场景支持多个“大脑”并存&#xff0c;每个…

C语言用两个函数求最大公约数和最小公倍数

目录 1【c语言】(函数)写两个函数,分别求两个整数的最大公约数和最小公倍数。在主函数中输入两个数&#xff0c;分别调用这两个函数&#xff0c;并输出结果 2代码: 3运行代码: 4总结: 1【c语言】(函数)写两个函数,分别求两个整数的最大公约数和最小公倍数。在主函数中输入两…

智能优化算法应用:基于金枪鱼群算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于金枪鱼群算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于金枪鱼群算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.金枪鱼群算法4.实验参数设定5.算法结果6.…

十一.约束(二)

约束 5.自增列:AUTO_INCREMENT5.1作用5.2关键字5.3特点和要求5.4如何指定自增约束5.5如何删除自增列5.6MySQL8.0新特性——自增变量的持久化 6.FOREIGN KEY 约束6.1作用6.2关键字6.3主表和从表/父表和子表6.4特点6.5添加外键约束6.6演示问题6.7约束等级6.8删除外键约束6.9开发场…

5G边缘计算:解密边缘计算的魔力

引言 你是否曾想过&#xff0c;网络可以更贴心、更智能地为我们提供服务&#xff1f;5G边缘计算就像是网络的小助手&#xff0c;时刻待命在你身边&#xff0c;让数字生活变得更加便捷。 什么是5G边缘计算&#xff1f; 想象一下&#xff0c;边缘计算就像是在离你最近的一层“云…

树与图的深度优先遍历、宽度优先遍历算法总结

知识概览 树是特殊的图&#xff0c;是无环连通图图分为有向图和无向图。因为无向图可以转化为有向图&#xff0c;树可以转化为图。因此本文讨论有向图。 树和图的存储&#xff1a; 邻接矩阵&#xff1a;空间复杂度&#xff0c;适合存储稠密图。邻接表&#xff1a;存储每个点可以…

SpringBoot3-核心原理

生命周期监听 场景&#xff1a;监听应用的生命周期 1. 监听器-SpringApplicationRunListener 自定义SpringApplicationRunListener来监听事件&#xff1b; 1.1. 编写SpringApplicationRunListener 实现类 1.2. 在 META-INF/spring.factories 中配置 org.springframework.boo…

Featured Based知识蒸馏及代码(3): Focal and Global Knowledge (FGD)

文章目录 1. 摘要2. Focal and Global 蒸馏的原理2.1 常规的feature based蒸馏算法2.2 Focal Distillation2.3 Global Distillation2.4 total loss3. 实验完整代码论文: htt

【数据库系统概论】第3章-关系数据库标准语言SQL(1)

文章目录 3.1 SQL概述3.2 学生-课程数据库3.3 数据定义3.3.1 数据库定义3.3.2 模式的定义3.3.3 基本表的定义3.3.4 索引的建立与删除3.3.5 数据字典 3.1 SQL概述 动词 分类 三级模式 3.2 学生-课程数据库 3.3 数据定义 3.3.1 数据库定义 创建数据库 tips&#xff1a;[ ]表…

图解LRU缓存

图解LRU缓存 OJ链接 介绍 LRU 缓存机制可以通过哈希表辅以双向链表实现&#xff0c;我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。 双向链表按照被使用的顺序存储了这些键值对&#xff0c;靠近尾部的键值对是最近使用的&#xff0c;而靠近头部的键值对是最久未…

论文笔记--Learning Political Polarization on Social Media Using Neural Networks

论文笔记--Learning Political Polarization on Social Media Using Neural Networks 1. 文章简介2. 文章概括3. 相关工作4. 文章重点技术4.1 Collection of posts4.1.1 数据下载4.1.2 数据预处理4.1.3 统计显著性分析 4.2 Classification of Posts4.3 Polarization of users 5…

碳排放预测 | 基于ARIMA和GM(1,1)的碳排放预测(Matlab)

目录 预测效果基本介绍模型描述ARIMA模型GM(1,1)模型 程序设计参考资料 预测效果 基本介绍 基于ARIMA和GM(1,1)的碳排放预测&#xff08;Matlab&#xff09; 基于ARIMA&#xff08;自回归移动平均模型&#xff09;和GM(1,1)&#xff08;灰色预测模型&#xff09;的碳排放预测是…

1-2B参数规模大模型使用心得及模型汇总

大模型时代&#xff0c;根据大模型缩放定律&#xff0c;大家通常都在追求模型的参数规模更大、训练的数据更多&#xff0c;从而使得大模型涌现出更多的智能。但是&#xff0c;模型参数越大部署压力就越大。即使有gptq、fastllm、vllm等推理加速方法&#xff0c;但如果GPU资源不…

go语言初体验1--使用go install

当安装后go语言后。 尝试编写go程序。 当使用 go install 命令&#xff0c;报错。 go: go install requires a version when current directory is not in a moduleTry go install jvmgo\ch01latest to install the latest version通过查找资料。 用命令&#xff1a; go env …

VS(Visual Studio)更改文件编码

vs默认编码是GB2312,更改为UTF-8 工具->自定义

039、转置卷积

之——增大高宽 杂谈 通常来说&#xff0c;卷积不会增大输入的高宽&#xff0c;通常要么不变&#xff0c;要么减半&#xff1b;如果想要直接padding来增加高宽&#xff0c;在不断的卷积过程中&#xff0c;padding的0越来越多&#xff0c;最后要做像素级的判断时候&#xff0c;由…

【SpringCloud笔记】(8)服务网关之GateWay

GateWay 概述简介 官网地址&#xff1a; 上一代网关Zuul 1.x&#xff1a;https://github.com/Netflix/zuul/wiki&#xff08;有兴趣可以了解一下&#xff09; gateway&#xff1a;https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/…

STM32F407-14.3.10-表73具有有断路功能的互补通道OCx和OCxN的输出控制位-1x011

如上表所示&#xff0c;MOE1&#xff0c;OSSR0&#xff0c;CCxE1&#xff0c;CCxNE1时&#xff0c;OCx与OCxN对应端口的输出状态取决于OCx_REF与极性选择&#xff08;CCxP&#xff0c;CCxNP&#xff09; 死区。 ------------------------------------------------------------…

浅析海博深造

文章目录 深造作用 留学种类 选专业 择校 申请流程 申请方式 深造作用 1、个人能力提升&#xff08;学术专业、语言、新文化或新生活方式&#xff09; 2、更好的职业发展&#xff08;起点更高、结交新朋友或扩大社交圈&#xff09; 3、北京上海落户优惠 4、海外居留福…

【VB测绘程序设计】案例4——简单的四则运算练习Select Case语句的使用(附源码)

【VB测绘程序设计】案例4——简单的四则运算练习(附源码) 文章目录 前言一、界面预览二、程序介绍总结前言 在新手学习VB程序设计中,四则运算是基础,通过设计的TexT、按钮、label等控件,定义变量,实现简单程序的编写,提高对VB程序的入门训练。 一、界面预览 二、程序介…