【算法】4. 寻找两个正序数组的中位数(多语言实现)

简介: 给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。算法的时间复杂度应该为 O(log (m+n)) 。

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中各个元素顺序位置不会变,数组2也一样。所以新数组的前半部分一定是数组1前面连续的一部分(可能是空)和数组2前面连续的一部分组成的,后半部分同理。所以其实只需要知道数组1在新数组前后两部分的切分位置,数组2同理。

题解:

rust

impl Solution {
    pub fn find_median_sorted_arrays(nums1: Vec<i32>, nums2: Vec<i32>) -> f64 {
        let (n1, n2) = (nums1.len(), nums2.len());

        if n1 > n2 {
            return Solution::find_median_sorted_arrays(nums2, nums1);
        }

        let (mut left, mut right) = (0, n1);

        // 中位数位置之和
        let mid_idx_sum = (n1 + n2 + 1) >> 1;

        while left < right {
            // 前一部分包含 nums1[0 .. idx1-1] 和 nums2[0 .. idx2-1]
            // 后一部分包含 nums1[idx1 .. n1-1] 和 nums2[idx2 .. n2-1]
            let idx1 = (left + right + 1) >> 1;
            let idx2 = mid_idx_sum - idx1;
            let num1_1 = nums1[idx1 - 1];
            let num2_2 = nums2[idx2];

            if num1_1 > num2_2 {
                right = idx1 - 1;
            } else {
                left = idx1;
            }
        }

        let (idx1, idx2) = (left, mid_idx_sum - left);

        // nums1_1, nums1_2, nums2_1, num2_2 分别表示 nums1[idx1-1], nums1[idx1], nums2[idx2-1], nums2[idx2]
        let nums1_1 = match idx1 {
            0 => i32::MIN,
            _ => nums1[idx1 - 1]
        };
        let nums1_2 = match idx1 {
            i if i == n1 => i32::MAX,
            _ => nums1[idx1]
        };
        let nums2_1 = match idx2 {
            0 => i32::MIN,
            _ => nums2[idx2 - 1]
        };
        let nums2_2 = match idx2 {
            j if j == n2 => i32::MAX,
            _ => nums2[idx2]
        };

        // median1:前一部分的最大值
        let median1 = nums1_1.max(nums2_1);
        // median2:后一部分的最小值
        let median2 = nums1_2.min(nums2_2);

        if (n1 + n2) & 1 == 0 {
            // 偶数,有两个中位数
            (median1 + median2) as f64 / 2.0
        } else {
            // 奇数,有一个中位数
            median1 as f64
        }
    }
}

go

func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
    n1, n2 := len(nums1), len(nums2)

    if n1 > n2 {
        return findMedianSortedArrays(nums2, nums1)
    }

    left, right := 0, n1

    // 中位数位置之和
    midIdxSum := (n1 + n2 + 1) >> 1

    for left < right {
        // 前一部分包含 nums1[0 .. idx1-1] 和 nums2[0 .. idx2-1]
        // 后一部分包含 nums1[idx1 .. n1-1] 和 nums2[idx2 .. n2-1]
        idx1 := (left + right + 1) >> 1
        idx2 := midIdxSum - idx1
        nums1N1 := nums1[idx1-1]
        nums2N2 := nums2[idx2]

        if nums1N1 > nums2N2 {
            right = idx1 - 1
        } else {
            left = idx1
        }
    }

    idx1, idx2 := left, midIdxSum-left

    // nums1N1, nums1N2, nums2N1, nums2N2 分别表示 nums1[idx1-1], nums1[idx1], nums2[idx2-1], nums2[idx2]
    var nums1N1 int
    if idx1 == 0 {
        nums1N1 = math.MinInt32
    } else {
        nums1N1 = nums1[idx1-1]
    }
    var nums1N2 int
    if idx1 == n1 {
        nums1N2 = math.MaxInt32
    } else {
        nums1N2 = nums1[idx1]
    }
    var nums2N1 int
    if idx2 == 0 {
        nums2N1 = math.MinInt32
    } else {
        nums2N1 = nums2[idx2-1]
    }
    var nums2N2 int
    if idx2 == n2 {
        nums2N2 = math.MaxInt32
    } else {
        nums2N2 = nums2[idx2]
    }

    // median1:前一部分的最大值
    var median1 int
    if nums1N1 > nums2N1 {
        median1 = nums1N1
    } else {
        median1 = nums2N1
    }
    // median2:后一部分的最小值
    var median2 int
    if nums1N2 < nums2N2 {
        median2 = nums1N2
    } else {
        median2 = nums2N2
    }

    if (n1+n2)&1 == 0 {
        // 偶数,有两个中位数
        return float64(median1+median2) / 2.0
    } else {
        // 奇数,有一个中位数
        return float64(median1)
    }
}

c++

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        const int n1 = nums1.size(), n2 = nums2.size();

        if (n1 > n2) {
            return findMedianSortedArrays(nums2, nums1);
        }

        int left = 0, right = n1;

        // 中位数位置之和
        const int midIdxSum = (n1 + n2 + 1) >> 1;

        while (left < right) {
            // 前一部分包含 nums1[0 .. idx1-1] 和 nums2[0 .. idx2-1]
            // 后一部分包含 nums1[idx1 .. n1-1] 和 nums2[idx2 .. n2-1]
            const int idx1 = (left + right + 1) >> 1;
            const int idx2 = midIdxSum - idx1;
            const int nums1N1 = nums1[idx1 - 1];
            const int nums2N2 = nums2[idx2];

            if (nums1N1 > nums2N2) {
                right = idx1 - 1;
            } else {
                left = idx1;
            }
        }

        const int idx1 = left, idx2 = midIdxSum - left;

        // nums1N1, nums1N2, nums2N1, nums2N2 分别表示 nums1[idx1-1], nums1[idx1], nums2[idx2-1], nums2[idx2]
        const int nums1N1 = (idx1 == 0 ? INT_MIN : nums1[idx1 - 1]);
        const int nums1N2 = (idx1 == n1 ? INT_MAX : nums1[idx1]);
        const int nums2N1 = (idx2 == 0 ? INT_MIN : nums2[idx2 - 1]);
        const int nums2N2 = (idx2 == n2 ? INT_MAX : nums2[idx2]);

        // median1:前一部分的最大值
        const int median1 = max(nums1N1, nums2N1);
        // median2:后一部分的最小值
        const int median2 = min(nums1N2, nums2N2);

        // 偶数,有两个中位数
        // 奇数,有一个中位数
        return ((n1 + n2) & 1) == 0 ? (median1 + median2) / 2.0 : median1;
    }
};

c

double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
    const int n1 = nums1Size, n2 = nums2Size;

    if (n1 > n2) {
        return findMedianSortedArrays(nums2, nums2Size, nums1, nums1Size);
    }

    int left = 0, right = n1;

    // 中位数位置之和
    const int midIdxSum = (n1 + n2 + 1) >> 1;

    while (left < right) {
        // 前一部分包含 nums1[0 .. idx1-1] 和 nums2[0 .. idx2-1]
        // 后一部分包含 nums1[idx1 .. n1-1] 和 nums2[idx2 .. n2-1]
        const int idx1 = (left + right + 1) >> 1;
        const int idx2 = midIdxSum - idx1;
        const int nums1N1 = nums1[idx1 - 1];
        const int nums2N2 = nums2[idx2];

        if (nums1N1 > nums2N2) {
            right = idx1 - 1;
        } else {
            left = idx1;
        }
    }

    const int idx1 = left, idx2 = midIdxSum - left;

    // nums1N1, nums1N2, nums2N1, nums2N2 分别表示 nums1[idx1-1], nums1[idx1], nums2[idx2-1], nums2[idx2]
    const int nums1N1 = (idx1 == 0 ? INT32_MIN : nums1[idx1 - 1]);
    const int nums1N2 = (idx1 == n1 ? INT32_MAX : nums1[idx1]);
    const int nums2N1 = (idx2 == 0 ? INT32_MIN : nums2[idx2 - 1]);
    const int nums2N2 = (idx2 == n2 ? INT32_MAX : nums2[idx2]);

    // median1:前一部分的最大值
    const int median1 = fmax(nums1N1, nums2N1);
    // median2:后一部分的最小值
    const int median2 = fmin(nums1N2, nums2N2);

    // 偶数,有两个中位数
    // 奇数,有一个中位数
    return ((n1 + n2) & 1) == 0 ? (median1 + median2) / 2.0 : median1;
}

python

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        n1, n2 = len(nums1), len(nums2)
        if n1 > n2:
            return self.findMedianSortedArrays(nums2, nums1)
        left, right = 0, n1
        # 中位数位置之和
        mid_idx_sum = (n1 + n2 + 1) >> 1
        while left < right:
            # 前一部分包含nums1[0..idx1 - 1]和nums2[0..idx2 - 1]
            # 后一部分包含nums1[idx1..n1 - 1]和nums2[idx2..n2 - 1]
            idx1 = (left + right + 1) >> 1
            idx2 = mid_idx_sum - idx1
            nums1_1 = nums1[idx1 - 1]
            nums2_2 = nums2[idx2]
            if nums1_1 > nums2_2:
                right = idx1 - 1
            else:
                left = idx1
        idx1, idx2 = left, mid_idx_sum - left
        # nums1_1, nums1_2, nums2_1, nums2_2分别表示nums1[idx1 - 1], nums1[idx1], nums2[idx2 - 1], nums2[idx2]
        infinty = 2 ** 32
        nums1_1 = (-infinty if idx1 == 0 else nums1[idx1 - 1])
        nums1_2 = (infinty if idx1 == n1 else nums1[idx1])
        nums2_1 = (-infinty if idx2 == 0 else nums2[idx2 - 1])
        nums2_2 = (infinty if idx2 == n2 else nums2[idx2])
        # median1:前一部分的最大值
        median1 = max(nums1_1, nums2_1)
        # median2:后一部分的最小值
        median2 = min(nums1_2, nums2_2)
        # 偶数,有两个中位数
        # 奇数,有一个中位数
        return (median1 + median2) / 2 if (n1 + n2) & 1 == 0 else median1

java

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        final int n1 = nums1.length, n2 = nums2.length;

        if (n1 > n2) {
            return findMedianSortedArrays(nums2, nums1);
        }

        int left = 0, right = n1;

        // 中位数位置之和
        final int midIdxSum = (n1 + n2 + 1) >> 1;

        while (left < right) {
            // 前一部分包含 nums1[0 .. idx1-1] 和 nums2[0 .. idx2-1]
            // 后一部分包含 nums1[idx1 .. n1-1] 和 nums2[idx2 .. n2-1]
            final int idx1    = (left + right + 1) >> 1;
            final int idx2    = midIdxSum - idx1;
            final int nums1N1 = nums1[idx1 - 1];
            final int nums2N2 = nums2[idx2];

            if (nums1N1 > nums2N2) {
                right = idx1 - 1;
            } else {
                left = idx1;
            }
        }

        final int idx1 = left, idx2 = midIdxSum - left;

        // nums1N1, nums1N2, nums2N1, nums2N2 分别表示 nums1[idx1-1], nums1[idx1], nums2[idx2-1], nums2[idx2]
        final int nums1N1 = (idx1 == 0 ? Integer.MIN_VALUE : nums1[idx1 - 1]);
        final int nums1N2 = (idx1 == n1 ? Integer.MAX_VALUE : nums1[idx1]);
        final int nums2N1 = (idx2 == 0 ? Integer.MIN_VALUE : nums2[idx2 - 1]);
        final int nums2N2 = (idx2 == n2 ? Integer.MAX_VALUE : nums2[idx2]);

        // median1:前一部分的最大值
        final int median1 = Math.max(nums1N1, nums2N1);
        // median2:后一部分的最小值
        final int median2 = Math.min(nums1N2, nums2N2);

        // 偶数,有两个中位数
        // 奇数,有一个中位数
        return ((n1 + n2) & 1) == 0 ? (median1 + median2) / 2.0 : median1;
    }
}

非常感谢你阅读本文~
放弃不难,但坚持一定很酷~
希望我们大家都能每天进步一点点~
本文由 二当家的白帽子:https://developer.aliyun.com/profile/sqd6avc7qgj7y 博客原创~

相关文章
|
7月前
|
算法 测试技术 C++
【动态规划】【中位数】【C++算法】1478. 安排邮筒
【动态规划】【中位数】【C++算法】1478. 安排邮筒
|
7月前
|
自然语言处理 Rust 算法
【算法】13. 罗马数字转整数(多语言实现)
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。 | 字符 | 数值 | |--|--| | I | 1 | | V | 5 | | X | 10 | | L | 50 | | C | 100 | | D | 500 | | M | 1000 | 例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。 通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1
【算法】13. 罗马数字转整数(多语言实现)
|
自然语言处理 Rust 算法
【算法】9. 回文数(多语言实现)
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。 回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。
|
算法 测试技术 C#
C++前缀和算法的应用:统计中位数为 K 的子数组
C++前缀和算法的应用:统计中位数为 K 的子数组
|
7月前
|
算法
【MATLAB】语音信号识别与处理:移动中位数滤波算法去噪及谱相减算法呈现频谱
【MATLAB】语音信号识别与处理:移动中位数滤波算法去噪及谱相减算法呈现频谱
104 2
|
7月前
|
算法
LeetCode算法题---无重复字符的最长子串、寻找两个正序数组的中位数(三)
LeetCode算法题---无重复字符的最长子串、寻找两个正序数组的中位数(三)
66 0
|
算法 C++
剑指offer(C++)-JZ41:数据流中的中位数(算法-排序)
剑指offer(C++)-JZ41:数据流中的中位数(算法-排序)
剑指offer(C++)-JZ41:数据流中的中位数(算法-排序)
|
5月前
|
人工智能 算法
算法金 | 平均数、众数、中位数、极差、方差,标准差、频数、频率 一“统”江湖
**统计学江湖概要** - **平均数(均值)**:数字的总和除以数量,代表集中趋势,如分赃时平均分配。 - **众数**:出现次数最多的数字,反映了最常见的值,如同一招式被频繁使用。 - **中位数**:排序后位于中间的值,反映数据的中心位置,如同武者武功的中等水平。 - **极差**:最大值减最小值,表示数据波动范围,类似武功最高与最低的差距。 - **方差**:衡量数据波动性,计算每个数值与均值差的平方和的平均数。 - **标准差**:方差的平方根,同单位的波动度量。 - **频数**:某个值出现的次数,如统计武器使用情况。 - **频率**:频数与总次数的比例,显示出现的相对频率。
104 2
算法金 | 平均数、众数、中位数、极差、方差,标准差、频数、频率 一“统”江湖
|
5月前
|
自然语言处理 Rust 算法
【算法】17. 电话号码的字母组合(多语言实现)
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
【算法】17. 电话号码的字母组合(多语言实现)
|
6月前
|
算法 自然语言处理 Rust
【算法】16. 最接近的三数之和(多语言实现)
给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。 返回这三个数的和。 假定每组输入只存在恰好一个解。