`

Leetcode-计算两个排序数组的中位数

阅读更多

题目描述

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

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

示例 1:

nums1 = [1, 3]
nums2 = [2]

中位数是 2.0

示例 2:

nums1 = [1, 2]
nums2 = [3, 4]

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

算法实现

我的实现思路

  • 根据元素总个数,计算出中位数的位置。
    • 如果总个数为奇数,则中位数取中间一个即可。
    • 如果总个数为偶数,则需要取中间两个数之和的平均值。
  • 假设桌上有两副已排好序的扑克牌,从上到下由小到大排序。那么
    • 从两副牌的顶部各取一张,进行比较后取最小的一张牌。
    • 从取出最小牌的那副再取一张,与另一副前一次抽取的牌进行比较,然后取最小的一张牌。
    • 如此循环,我们拿到的牌将是从小到大的顺序,直至拿到牌的数量与我们要找的中位数位置相同即可停止。
    • 此时,如果判断元素总数为偶数个,则再取一张牌后停止。
  • 最后,如果是奇数个元素,则直接返回。如果是偶数个元素,则返回两个数之和的平均值。
/**
 * 个人实现版本.
 * 
 * @param nums1
 * @param nums2
 * @return
 */

public static double findMedianSortedArraysV1(int[] nums1, int[] nums2) {
    int[] resultArray = new int[2];
    int idx1 = 0;
    int idx2 = 0;
    int currentCount = 0;

    int size1 = (nums1 == null) ? 0 : nums1.length;
    int size2 = (nums2 == null) ? 0 : nums2.length;

    boolean evenResult = true// 总数是否为偶数个
    int medianNum = (size1 + size2) / 2// 中位数的位置
    if ((size1 + size2) % 2 != 0) {
        medianNum++;
        evenResult = false// 总数为奇数个
    }
    if (medianNum == 0) {
        throw new IllegalArgumentException("median num not found");
    }

    int currentValue = 0;
    while (true) {
        if (idx1 <= size1 - 1 && idx2 <= size2 - 1) {
            if (nums1[idx1] <= nums2[idx2]) {
                currentValue = nums1[idx1++];
            } else {
                currentValue = nums2[idx2++];
            }
            currentCount++;
        } else if (idx1 <= size1 - 1) {
            currentValue = nums1[idx1++];
            currentCount++;
        } else if (idx2 <= size2 - 1) {
            currentValue = nums2[idx2++];
            currentCount++;
        }

        // 相等则找到中位数
        if (currentCount == medianNum) {
            resultArray[0] = currentValue;
            if (!evenResult) { // 如果总数为偶数,需要找到中间的2个数取平均值
                break;
            }
        } else if (currentCount > medianNum && evenResult) { // 找到第二数
            resultArray[1] = currentValue;
            break;
        }
    }

    double resultValue = evenResult ? Double.valueOf(resultArray[0] + resultArray[1]) / 2 : Double.valueOf(resultArray[0]);

    return resultValue;
}

个人实现的版本,时间复杂度为O((m+n)/2),比题目的要求慢一些,不过思路相对清晰一些。

习题答案

个人理解如下:

假设有数组A,长度为m,任意位置i将数组分为左右两部分。数组B,长度为n,任意位置j将数组分为左右两部分。

那么i>=0,且i<=m。j>=0,且j<=n。i和j可以理解为数组从小到大的第几个数,例如i=1代表A[0],即数组A的第一个数。

如果要找到A和B各自的中位数,需要满足i=m-i,j=n-j,即左右两部分的个数是相同的,此时中间的数即为中位数。当然,实现的时候仍需要考虑奇数、偶数个数组元素的问题。

由于我们是在A和B两个数组合并之后找中位数,因此找到中位数需要满足i+j=m-i+n-j(或:m-i+n-j+1,奇数个元素的情况)。

另外,题目要求的时间复杂度是log(m+n),所以很容易想到二分查找(如折半查找、二叉查找树)。所以问题的关键点就是,在不合并数组并做好排序(时间复杂度为O(m)、O(n)或O(m+n),肯定不满足需求)的情况下,如何利用二分查找来实现该算法。

由前面我们推导出的公式可知,j=(m+n)/2-i或j=(m+n+1)/2-i。

事实上,如果要简单的理解可以不用这么麻烦的推导。

假设数组元素的总个数为奇数,那么中位数必然在第(m + n + 1) / 2个。

此时,要找到中位数,显然i+j=(m + n + 1) / 2,这个结果与上述的推导是一致的。有了这个公式,二分查找就好办了。分而治之,先计算出数组A的中间位置i=(0+m)/2。那么,此时j就可以通过前面的公式计算出来了。

二分查找时,如果满足B[j−1]<=A[i]且A[i−1]<=B[j],则表示找到中位数了,结束查找。如果不满足,则在i的前或后半部分继续进行上述的二分查找,直至满足条件即可。

为什么说满足B[j−1]<=A[i]且A[i−1]<=B[j],则表示找到中位数了?

因为A[i−1]+B[j−1]代表数组的一半元素,共有i+j个。那么只要满足最后一个条件,我们就找到中位数了。最后一个条件,就是A[i−1]和B[j−1]都要小于等于另一半元素的值,这里我们只要保证同时小于另一半的最小值A[i]和B[j](或者说是下一个值)即可。

/**
 * 习题答案
 * 
 * @param A
 * @param B
 * @return
 */

public double findMedianSortedArrays(int[] A, int[] B) {
    int m = A.length;
    int n = B.length;
    if (m > n) { // to ensure m<=n
        int[] temp = A; A = B; B = temp;
        int tmp = m; m = n; n = tmp;
    }
    int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
    while (iMin <= iMax) {
        int i = (iMin + iMax) / 2;
        int j = halfLen - i;
        if (i < iMax && B[j-1] > A[i]){
            iMin = iMin + 1// i is too small
        }
        else if (i > iMin && A[i-1] > B[j]) {
            iMax = iMax - 1// i is too big
        }
        else { // i is perfect
            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;
}

算法实现

https://github.com/qiuzj/leetcode/blob/master/src/main/java/cn/javaee/leetcode/q4/median_of_two_sorted_arrays/MedianOfTwoSortedArrays.java

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

 

 

转载请注明来源:http://zhanjia.iteye.com/blog/2427289

 

个人公众号

二进制之路

 

分享到:
评论

相关推荐

    python-leetcode题解之004两个排序数组的中位数

    python python_leetcode题解之004两个排序数组的中位数

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

    本题通过模拟归并排序的合并过程来高效地求解两个正序数组的中位数。这种方法不仅简洁明了,而且效率高,适合初学者理解和掌握。在实际应用中,这种方法也可以扩展应用于更复杂的场景,例如多个有序数组的中位数求解...

    LeetCode刷题记录(简单-数组)

    删除排序数组中的重复项 (C级) **题解**: 给定一个已排序的数组,去除重复元素并在原数组上进行修改,返回新的数组长度。 **算法**: - 使用双指针法:一个快指针用于遍历数组,一个慢指针用于跟踪不重复元素的...

    php-leetcode题解之删除排序数组中的重复项.zip

    在本压缩包“php-leetcode题解之删除排序数组中的重复项.zip”中,主要包含的是使用PHP解决LeetCode上的一道经典问题——删除排序数组中的重复项。这道题目要求我们在保持数组排序的前提下,移除重复的元素,只保留...

    leetcode卡-Array-LeetCode-Solution:数组-LeetCode-解决方案

    如"寻找旋转排序数组中的最小值"(Find Minimum in Rotated Sorted Array)。 4. **滑动窗口**:在给定大小的窗口内处理数组元素,常用于求解最大/最小值、频率统计等问题。如"最宽的水平条"(Largest Rectangle in...

    算法-leetcode-剑指offer上的题很多

    - **寻找数组中的中位数(Median of Two Sorted Arrays)**: 在两个排序数组中找到中位数。 - **快速选择算法(Kth Largest Element)**: 选择一个无序数组中的第k大的元素。 #### 二分搜索扩展 - **二分搜索变体(First...

    leetcode-cpp刷题

    - 求两个有序数组的中位数。 - 实现思路:通过比较两个数组的中间值来逐步缩小查找范围。 - **2.1.6 Longest Consecutive Sequence** - 找出数组中最长的连续子序列。 - 实现思路:可以使用哈希表存储每个元素...

    leetcode-tag-array

    10. **位运算**:在数组中利用位运算进行高效计算,例如快速幂、求模等。 在LeetCode的“leetcode-tag-array”标签下,你会遇到各种类型的数组问题,它们将锻炼你对数组特性的理解和运用,提高你的算法思维能力。...

    java面试题-leetcode题解之第4题寻找两个正序数组的中位数.zip

    LeetCode第4题要求在两个已排序的数组中找到中位数。这是一个双指针问题,可以通过同时遍历两个数组来解决。以下是核心思路: 1. 初始化两个指针,分别指向两个数组的开头。 2. 比较两个指针所指元素的大小,根据...

    python-leetcode面试题解之寻找两个正序数组的中位数.zip

    "寻找两个正序数组的中位数"是LeetCode上的一道经典题目,涉及到数组操作和排序算法。本题解将详细探讨这个问题的解决方案及其背后的逻辑。 首先,我们需要明确问题的要求:给定两个长度为n和m的正序数组nums1和...

    c#-Leetcode面试题解之第4题寻找两个正序数组的中位数.zip

    1. **合并与排序**:最直观的方法是将两个数组合并成一个大的正序数组,然后找到中位数。但这种方法的时间复杂度较高,不适用于大数据量的情况。 2. **二分查找法**:更高效的方法是利用二分查找的思想,结合两个...

    leetcode-master.zip

    需要在两个已排序的数组中找到中位数,如果两个数组合并后的数组有偶数个元素,中位数就是中间两个数的平均值。这个问题通常用二分查找法解决,需要对两个有序数组进行高效的交互搜索。 【标签】"LeetCode C code" ...

    C语言-C语言编程基础之leetcode题解第4题寻找两个正序数组的中位数.zip

    在本压缩包文件中,我们聚焦于C语言编程的基础知识,特别是通过解决LeetCode上的第四题——寻找两个正序数组的中位数。这是一道经典的算法问题,它要求我们理解数组的操作,以及如何在复杂的数据结构中找到关键的...

    c++-c++编程基础之leetcode题解第4题寻找两个正序数组的中位数.zip

    在两个正序数组中找中位数,我们需要合并这两个数组并找出中位数,但要求是尽可能地减少操作次数,因为这直接影响到算法的效率。 C++作为一门强大的编程语言,它提供了丰富的数据结构和算法工具来解决这类问题。...

    _leetcode-python.pdf

    - Median of Two Sorted Arrays: 给定两个排序的数组,找到它们的中位数。解决这个问题的高效算法需要注意时间复杂度。 - Longest Palindromic Substring: 要求找出字符串中的最长回文子串。这通常可以通过中心扩展...

    leetcode卡-LeetCode---Arrays-and-Strings:LeetCode---数组和字符串

    例如,找到数组中的最长连续序列、查找两个有序数组的中位数等题目,都可以用到双指针。 4. **排序与查找**:快速排序、归并排序、二分查找等经典算法在数组问题中非常常见。理解这些排序和查找算法的工作原理及其...

    leetcode数组下标大于间距-leetcode-hot100:leetcode-hot100-python

    两个有序数组的中位数。这道题再练习一下。二分搜索,中位数的定义,细节题。 无重复字符的最长子串。滑动窗口常规题,hashmap,记录窗口内每个字符的 ind。 编辑距离。动态规划题。需要再练习 接雨水。单调栈或者两...

    寻找两个正序数组的中位数1

    在给定的编程问题“寻找两个正序数组的中位数1”中,我们需要找到两个已排序的整数数组(nums1 和 nums2)的中位数。这个问题是LeetCode平台上的一个问题,它考察了算法设计和理解数组特性的能力。下面我们将详细...

    LeetCode初级算法-数组(手写)

    问题描述:在给定的排序数组中,我们需要移除所有重复的元素,使得每个元素只出现一次,并且不使用额外的数组空间,在原地修改数组完成。 解决方法:利用双指针技巧,一个指针用于遍历数组,另一个指针用于放置下一...

    多线程leetcode-leetcode-go:leetcode-go

    两个有序数组的中位数 难的 0005 最长回文子串 中等的 0006 之字形转换 中等的 0007 反转整数 简单的 0008 字符串到整数 (atoi) 中等的 0009 回文数 简单的 0010 正则表达式匹配 难的 0011 盛水最多的容器 中等的 ...

Global site tag (gtag.js) - Google Analytics