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

目录

  • 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)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

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

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

案例149:基于微信小程序的家庭财务管理系统的设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

Linux bridge开启hairpin模拟测试macvlan vepa模式

看到网上介绍可以通过Linux bridge 开启hairpin方式测试macvlan vepa模式&#xff0c;但是没有找到详细资料。我尝试测试总提示错误信息&#xff0c;无法实现&#xff0c;经过几天的研究&#xff0c;我总算实现模拟测试&#xff0c;记录如下&#xff1a; 参考 1.Linux Macvla…

展望2023年CSDN博客之星评选

目录 1 前言2 博客的意义3 人工智能对博客的影响4 AI 技术下的成长与分享5 技术的探索6 博客之星评选对于技术人的激励作用7 结语 1 前言 当我们回顾过去&#xff0c;博客不仅仅是一种记录生活、分享经验的方式&#xff0c;更是一个见证自我成长与进步的平台。站在2023年度 CS…

计算机是如何工作的(下)

4. 编程语言&#xff08;Program Language&#xff09; 本块内容主要是还原下我们已经熟悉的编程语言&#xff0c;即编程语言是如何和 CPU 指令对应起来的。 4.1 程序&#xff08;Program&#xff09; 所谓程序&#xff0c;就是一组指令以及这组指令要处理的数据。狭义上来说&…

【数据结构入门精讲 | 第十四篇】散列表知识点及考研408、企业面试练习(1)

在上一篇中我们进行了树的专项练习&#xff0c;在这一篇中我们将进行散列表知识点的学习。 目录 概念伪代码线性探测法平方探测法查找成功的平均查找长度查找失败的平均查找长度判断题选择题 概念 散列表&#xff08;Hash Table&#xff09;&#xff0c;也被称为哈希表或散列映…

向量投影:如何将一个向量投影到矩阵的行向量生成子空间?

向量投影&#xff1a;如何将一个向量投影到矩阵的行向量生成子空间&#xff1f; 前言 本问题是在学习Rosen梯度投影优化方法的时候遇到的问题&#xff0c;主要是对于正交投影矩阵(NT(NNT)-1N)的不理解&#xff0c;因此经过查阅资料&#xff0c;学习了关于向量投影的知识&…

嵌入式硬件电路原理图之跟随电路

描述 电压跟随电路 电压跟随器是共集电极电路&#xff0c;信号从基极输入&#xff0c;射极输出&#xff0c;故又称射极输出器。基极电压与集电极电压相位相同&#xff0c;即输入电压与输出电压同相。这一电路的主要特点是&#xff1a;高输入电阻、低输出电阻、电压增益近似…

Ubuntu:VS Code上C++的环境配置

使用 VSCode 开发 C/C 程序 , 涉及到 工作区的.vscode文件夹下的3个配置文件&#xff08;均可以手动创建&#xff09; : ① tasks.json : 编译器构建 配置文件 ; ② launch.json : 调试器设置 配置文件 ; ③ c_cpp_properties.json : 编译器路径和智能代码提示 配置文件 ;…

爬虫工作量由小到大的思维转变---<第二十三章 Scrapy开始很快,越来越慢(医病篇)>

诊断篇https://blog.csdn.net/m0_56758840/article/details/135170994?ops_request_misc%257B%2522request%255Fid%2522%253A%2522170333243316800180644102%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id1703332433168001806441…

JavaOOP篇----第十四篇

系列文章目录 文章目录 系列文章目录前言一、Hashcode的作用二、Java的四种引用,强弱软虚三、Java创建对象有几种方式?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码…

mac m1芯片 pytorch安装及gpu性能测试

pytorch 使用mac的m1芯片进行模型训练。 #小结&#xff1a;在数据量小和模型参数少&#xff0c;batch_size小时&#xff0c;cpu训练更快&#xff08;原因&#xff1a;每次训练时数据需要放入GPU中&#xff0c;由于batch_size小。数据放入gpu比模型计算时间还长&#xff09…

SpringIOC之AbstractMessageSource

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

K8S 外部访问配置、 Ingress、NodePort

将K8S部署应用提供给外部访问一般有三种方式&#xff1a; NodePort 暴露端口到节点&#xff0c;提供了集群外部访问的入口LoadBalancer 需要负载均衡器&#xff08;通常都需要云服务商提供&#xff0c;裸机可以安装 METALLB 测试&#xff09;Ingress 统一管理 svc的外部访…

Bloom过滤器

Bloom过滤器 一、概述二、原理三、优缺点1. 优点2.缺点 四、Bloom过滤器在比特币中的应用五、项目应用步骤1. pom.xml引入依赖2. 样例代码 六、Java版简易实现 一、概述 Bloom过滤器是一个允许用户描述特定的关键词组合而不必精确表述的基于概率的过滤方法。它能让用户在有效搜…

详解Vue3中的内置组件(transition)

本文主要介绍Vue3中的内置组件&#xff08;transition&#xff09;的普通写法和setup写法。 目录 一、在普通写法中使用内置组件&#xff08;transition&#xff09;二、在setup写法中使用内置组件&#xff08;transition&#xff09;三、使用注意项 在Vue3中&#xff0c;内置了…

Linux poll 和 select 机制

poll select 介绍 使用非阻塞 I/O 的应用程序常常使用 poll, select, 和 epoll 系统调用. poll, select 和 epoll 本质上有相同的功能: 每个允许一个进程来决定它是否可读或者写一个 或多个文件而不阻塞. 这些调用也可阻塞进程直到任何一个给定集合的文件描述符可用来 读或写.…

Nessus详细安装-windows (保姆级教程)

Nessus描述 Nessus 是一款广泛使用的网络漏洞扫描工具。它由 Tenable Network Security 公司开发&#xff0c;旨在帮助组织评估其计算机系统和网络的安全性。 Nessus 可以执行自动化的漏洞扫描&#xff0c;通过扫描目标系统、识别和评估可能存在的安全漏洞和弱点。它可以检测…

使用 Spring Boot + MyBatis开发需要注意的事项以及开发模版

前言&#xff1a; 注意&#xff0c;本篇不适用于有相关开发经验的开发者&#xff0c;作为一个在职开发者&#xff0c;我经常在完成从0-1的模块&#xff0c;也就是从数据库表开始到创建实体类&#xff0c;以及dao层&#xff0c;Service层等业务需要添加相关注解&#xff0c;这样…

使用office打开word文档时候提示错误:0x426-0x0的解决方案

在使用office打开word文档时候提示错误&#xff1a;0x426-0x0。如下图&#xff1a; 昨天还用的好好的&#xff0c;怎么今天就不行了&#xff1f;为什么呢&#xff1f; 更多工作中遇到问题见&#xff1a;凯哥BK 这个错误导致office无法启动通常是由于office软件所依赖的服务无…