zoukankan      html  css  js  c++  java
  • 【LeetCode】4. 寻找两个有序数组的中位数

    题目

    给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。

    请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

    你可以假设 nums1 和 nums2 不会同时为空。

    示例

    示例一

    nums1 = [1, 3]

    nums2 = [2]

    则中位数是 2.0

    示例二

    nums1 = [1, 2]

    nums2 = [3, 4]

    则中位数是 (2 + 3)/2 = 2.5

    解题

    中位数:一组数组中间位置的数,数组长度为奇数 = n / 2,数组长度为偶数 ( (n/2) + (n/2 +1) ) / 2
    时间复杂度要求:O(log(m + n))
    解法一、合并排序后取中值
    1. 将两个数据合并排序
    2. 根据合并后长度是奇数或偶数取中值
    public double FindMedianSortedArrays(int[] nums1, int[] nums2)
    {
        var result = new int[nums1.Length + nums2.Length];
    
        int i = 0, l = 0, r = 0;
        while (l < nums1.Length || r < nums2.Length)
        {
            if (r >= nums2.Length || (l < nums1.Length && nums1[l] < nums2[r]))   //右边数据扫描完或左边数据没扫描完并且小于右边
            {
                result[i++] = nums1[l++];
            }
            else
            {
                result[i++] = nums2[r++];
            }
        }
    
        if (result.Length % 2 == 0)
    
        {
            return (result[result.Length / 2 - 1] + result[result.Length / 2]) / 2.0d;
        }
        return result[result.Length / 2];
    }
    View Code

    时间复杂度:O(m+n) 

    能求解,但时间复杂度不符合条件

    解法二、扫描查找

    解法一需要长度为(m+n)的数组,这个数组只是为了最后取值所用,我们需要两个数据中位数,只需要扫描到n/2,后面的扫描是没有需要

    1. 用合并排序的方法扫描数据
    2. 扫描到n/2返回值
    public double FindMedianSortedArrays(int[] nums1, int[] nums2)
    {
        int len = nums1.Length + nums2.Length;
        int previous = 0, current = 0;
        int l = 0, r = 0;
    
        for (int i = 0; i <= len / 2; i++)
        {
            previous = current;
    
                if (r >= nums2.Length || (l < nums1.Length && nums1[l] < nums2[r])) //右边数据扫描完或左边数据没扫描完并且小于右边
            {
                current = nums1[l++];
            }
            else
            {
                current = nums2[r++];
            }
        }
    
        if ((len % 2) == 0) return (previous + current) / 2.0d; //长度为偶数
    
        return current;
    }
    View Code

    减少了一半的查询,只需要额外的5个字段。时间复杂度:O((m+n)/2),还是没达到题目要求

    解法三、二分查找

    要想达到log级别时间复杂度,只能从两个数组是有序的这个条件下手,有序数据的查找应该从二分查找下手。

    m = num1.length , n = num2.length
    k = (m + n) / 2;
    本题转换为寻找两个数组第 k 小的数

    1. 奇数情况
        1.1 初始化两个变量指向两个数组中间 i = m / 2; j = n / 2;
        1.2 A[i] <= B[j] 则A[i]前面的数字都可以丢弃,这时候变成求A[i] ~ A[n] 和 B[0] ~ B[n]两个数组第(k - i -1)小的数
        1.3 A[i] > B[j] 则B[j]前面的数字都可以丢弃,这时候变成求A[0] ~ A[n] 和 B[j] ~ B[n]两个数组第(k - j - 1)小的数
        1.4 以此类推
        1.5 k == 0,当前两个数组第一个数小的那个就是中位数
        1.6 注意边界问题,数组长度为空或1,跳出递归的条件
    2. 偶数情况
        ((k - 1) + k) /2
     
    public double FindMedianSortedArrays(int[] nums1, int[] nums2)
    {
        if (nums1.Length < nums2.Length) return FindMedianSortedArrays(nums2, nums1);
    
        if (nums1 == null || nums1.Length <= 0) return FindMedian(nums2);
    
        if (nums2 == null || nums2.Length <= 0) return FindMedian(nums1);
    
        /*
            m = num1.length , n = num2.length
            k = (m + n) / 2;
            本题转换为寻找两个数组第 k 小的数
    
            1. 奇数情况
                1.1 初始化两个变量指向两个数组中间 i = m / 2; j = n / 2;
                1.2 A[i] <= B[j] 则A[i]前面的数字都可以丢弃,这时候变成求A[i] ~ A[n] 和 B[0] ~ B[n]两个数组第(k - i -1)小的数
                1.3 A[i] > B[j] 则B[j]前面的数字都可以丢弃,这时候变成求A[0] ~ A[n] 和 B[j] ~ B[n]两个数组第(k - j - 1)小的数
                1.4 以此类推
                1.5 k == 0,当前两个数组第一个数小的那个就是中位数
                1.6 注意边界问题,数组长度为空或1,跳出递归的条件
            2. 偶数情况
                ((k - 1) + k) /2
        */
        var len = nums1.Length + nums2.Length;
    
        if (len % 2 == 1) return FindMedianSortedArrays(nums1, 0, nums1.Length, nums2, 0, nums2.Length, len / 2);
    
        return (FindMedianSortedArrays(nums1, 0, nums1.Length, nums2, 0, nums2.Length, len / 2 - 1) + FindMedianSortedArrays(nums1, 0, nums1.Length, nums2, 0, nums2.Length, len / 2)) / 2.0d;
    }
    
    public double FindMedianSortedArrays(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k)
    {
        //保证长的数组在前
        if ((end1 - start1) < (end2 - start2)) return FindMedianSortedArrays(nums2, start2, end2, nums1, start1, end1, k);
    
        if (k == 0) return Math.Min(nums1[start1], nums2[start2]);
    
        if ((end1 - start1) <= 1) return Math.Max(nums1[start1], nums2[start2]); //第一个数组长度为1的情况
    
        if ((end2 - start2) <= 1)   //第二数组长度为1的情况
        {
            if (nums1[start1 + k] <= nums2[start2]) return nums1[start1 + k];
    
            return Math.Max(nums1[start1 + k - 1], nums2[start2]);
        }
        if (k == 1) //k == 1,即第二小的数字,比较两组数组前面2个元素即可得到结果
        {
            if (nums1[start1 + 1] <= nums2[start2]) return nums1[start1 + 1];
    
            if (nums1[start1] >= nums2[start2 + 1]) return nums2[start2 + 1];
    
            return Math.Max(nums1[start1], nums2[start2]);
        }
    
        var i = Math.Min((end1 - start1), k) / 2;
        var j = Math.Min((end2 - start2), k) / 2;
    
        if (nums1[start1 + i] < nums2[start2 + j]) return FindMedianSortedArrays(nums1, start1 + i, end1, nums2, start2, end2, k - i);
    
        return FindMedianSortedArrays(nums1, start1, end1, nums2, start2 + j, end2, k - j);
    }
    
    public double FindMedian(int[] nums)
    {
        if (nums == null || nums.Length <= 0) throw new Exception("no result");
    
        if (nums.Length % 2 == 0) return (nums[nums.Length / 2 - 1] + nums[nums.Length / 2]) / 2.0d; ;
    
        return nums[nums.Length / 2];
    }
    View Code

     LeetCode提交通过

    相关知识

    1. 二分查找
    2. 有序数组里查找第k大/小算法
    3. 分治法

     来源:力扣(LeetCode)

     链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays

  • 相关阅读:
    sql server 2008数据复制方法
    排错技能:任务管理器中追踪某w3wp.exe是哪个IIS站点的application pool
    SplendidCRM中给来自EditView中的listbox控件设置选中值或数据源
    jQuery String Functions
    [转]jquery getJSON 数据联动(采用序列化和反序列化获取数据) .
    [转]javascript eval函数解析json数据时为什加上圆括号eval("("+data+")")
    深入理解C语言
    Qt回忆录之配置开发环境
    360电话面试
    浅谈C++设计模式之单例模式
  • 原文地址:https://www.cnblogs.com/WilsonPan/p/11702708.html
Copyright © 2011-2022 走看看