一.题目链接:https://leetcode.com/problems/median-of-two-sorted-arrays

二.题目大意:

  给定两个排序过的数组,求出两个数组的中位数,要求时间复杂度为O(log(m+n)).

.题解:

  这是我在LeetCode上做的第一道hard级别的题目,没想到难度系数达到了5!被虐哭了。。。言归正传,这道题目的解题思路看起来并不难,难点在于它限定了时间复杂度,而且是对数级别的复杂度(说明优化程度已经相当高了)。这里,我给出三种解决方案,当然前两种时间复杂度并不满足要求(仅供参考思路);只有第三种方案符合题目要求:

方法1:

  看到这种题目,首先想到的就是对两个数组进行合并,合并成一个有序的数组,然后根据数组元素个数的奇偶性求中位数即可。此处的排序方法有多种,最适合该题目的排序算法应该是归并排序。对于归并排序(默认升序),只需定义2个指针pa和pb分别指向数组a(长度为m)和数组b(长度为n),如果数组a的当前元素小,则*pa赋值给新数组,并且pa++;否则,*pb赋值给新数组,并且pb++;这么遍历指导遍历完某个数组,剩下的部分直接拼接到新数组末尾。最后找中位数。这么做的话时间复杂度为O(m+n),空间复杂度为O(1)。

方法2:

  我们想要的仅仅是数组的中位数,而排序操作实际上消耗的代价太大,而我们没必要消耗如此大的代价。所以,此处不必对数组进行合并与排序,但还是借鉴方法1的思想,定义一个计数器cnt,每当*pa<*pb时,cnt++,pa++;每当*pa>=*pb,cnt++,pb++;当cnt==k时们就能找到相当于合并后数组的第k大的数了(此处包括本文所指的第k大的数指的是在数组中位置排序为k的数,而不是按大小来算的)。而中位数就是去中间大的那个数。这么做的话,时间复杂度也是O(m+n),空间复杂度O(1)。但是平均时间复杂度肯定是比方法1要小的。所以,方法2效率更好一些;遇到类似题目时,应该优先考虑方法2。

方法3:

  前两种方法,都不能满足题目的要求。到底怎样才能找到指数级别的方法呢?首先我们来求一个更一般化的问题:求有序数组中第k个元素。而求有序数组的中位数实际就是这个问题的一个特例。具体一点说,假设数组的长度为n,当n为奇数时,即求第(n/2+1)个元素,当n为偶数时,即求第(n/2+1)和第(n/2)个元素,然后取他们的均值。下面我们从两个角度来分析:

  1.如何搜索两个有序数组中第k个元素呢,这里又有个技巧。假设数组都是从小到大排列,对于第一个数组中前p个元素和第二个数组中前q个元素,我们想要的最终结果是:(1)p+q等于k,(2)第一个数组中第p个元素(即A[p-1])和第二个数组中第q-1个元素(即B[q-2])都小于等于总数组第k个元素(或者第一个数组中第p-1个元素(即A[p-2])和第二个数组中列q个元素(即B[q-q]))。因为总数组中,必然有k-1个元素小于等于第k个元素。这样第p个元素或者第q个元素就是我们要找的第k个元素

  2.如果我们每次都能够删除一个一定在k位置之前的元素,那么我们需要进行k次。但是如果我们每次都删除一半呢?(即p和q都是k/2级别的)要达到log级别,我们应该充分利用数组的有序性,也就是类似于二分查找。

结合角度1和角度2,我们可以通过二分法将问题规模缩小,假设p=k/2,则q=k-p=k/2,且p+q=k。这样A[0]~A[p-1]共有k/2个元素,如果第一个数组第p个元素小于第二个数组第q个元素(即A[k/2-1] < B[k/2-1]),我们不确定第二个数组中第q个元素是大了还是小了(即B[k/2-1]的大小不确定),但第一个数组的前p个元素肯定都小于目标(反证法可证),所以我们将第一个序列前p个元素全部抛弃(A[0]~A[k/2 -1]全部抛弃),形成一个较短的新数组(从A[k/2]开始)。然后,用新数组替代原先的第一个数组,再找其中的第k-p个元素(因为我们已经排除了p个元素,k需要更新为k-p,相当于总数组的第k个位置变成了总数组第k-p个位置(总数组变小了)),依次递归。同理,如果第一个数组中的第p个元素大于第二个数组中的第q个元素,我们则抛弃第二个数组的前q个元素(B[0]~B[k/2 -1]全部抛弃)。递归的终止条件有如下几种:

                                              (1)较短数组中的所有元素都被抛弃,则返回较长数组中的第k个元素(在数组中下标是k-1)。

                                              (2)第一个数组中的第p个元素等于二数组中第q个元素(即A[k/2-1] == B[k/2 - 1]),此时第一个数组中的第p个元素和二数组中第q个元素就是总数组中的第k个元素。(此时返回A[k/2-1]或者B[k/2-1]都可以)

                                              (3)k == 1时,此时k不能再继续更新了,返回min(A[0],B[0])。(即本次传参时的数组A、B的起始位置)、

此外,还要注意:每次递归不仅要更新数组起始位置(起始位置之前的元素被抛弃),也要更新k的大小(扣除被抛弃的元素)!代码如下:

class Solution {
public:
double findMedianSortedArrays(const vector<int>& A, const vector<int>& B) {
const int m = A.size();
const int n = B.size();
int total = m + n;
if (total & 1)//判断数组长度是否为奇数
return find_kth(A.begin(), m, B.begin(), n, total / 2 + 1);
else
return (find_kth(A.begin(), m, B.begin(), n, total / 2)+ find_kth(A.begin(), m, B.begin(), n, total / 2 + 1)) / 2.0;
}
private:
static int find_kth(std::vector<int>::const_iterator A, int m,std::vector<int>::const_iterator B, int n, int k) {
//始终保证m小于等于n
if (m > n)
return find_kth(B, n, A, m, k);//此处一定不要忘记加return
if (m == 0)
return *(B + k - 1);
if (k == 1)
return min(*A, *B);
//相当于把k分成p和q两部分
int ia = min(k / 2, m), ib = k - ia;
if (*(A + ia - 1) < *(B + ib - 1))
return find_kth(A + ia, m - ia, B, n, k - ia);
else if (*(A + ia - 1) > *(B + ib - 1))
return find_kth(A, m, B + ib, n - ib, k - ib);
else
return A[ia - 1];
}
};

该方法的时间复杂度为O(log(m+n)),空间复杂度为O(log(m+n))(递归过程中每次调用都为O(1),总共为O(log(m+n))),符合题目要求。

注意:

1.当数组A的长度小于k/2时,此时选择p=m;那么此时B的长度一定是大于k/2的,并且舍去的一定是B的k/2个元素。此处利用反证法证明:

  假设第K位置的元素在B的前k/2中,例如位置在索引i(i <= k/2-1)那么A必然拥有前k中的k -(i+1)个元素,而i <= k/2-1,则 i+1 <= k/2  , k - (i+1) >= k/2与条件:A的元素不够k/2矛盾,所以假设不成立,得证。

所以此时舍去的必然是B的k/2个元素,下一步迭代的过程中k也会变小所以对于更新后的k,A的长度还是小于k/2的话,则会继续删除B的k/2个元素。

2.总的来说,删除元素的过程主要包括三种类型:

                    (1).A[k/2-1] < B[k/2-1],此时删除B的前k/2个元素。

                    (2)B[k/2-1] < B[k/2-1],此时删除A的前k/2个元素。

                    (3)A的元素个数小于k/2的话,此时删除B的前k/2个元素。 (不过这一过程并没有在程序中显试的表现出来,而是隐含在程序处理的过程中,此处说明这一步只是为了更好的理解本算法;实际上

int ia = min(k / 2, m)

  这行代码就是这一点的体现。)

3.函数find_kth中的参数m、n和k,都表示的是数字的位置,而不是数组的下标;实际数组的下标要再减去1。

=====================================================附录====================================================================

1.A[k/2-1]<B[k/2-1]时,总数组第k位置的数,会有三种情况:

(1)在A[k/2]~A[m-1]这一部分中。例如:

A={2,3,5}

B={1,7,8}

找到第4位置上的数,此时为5。

(2)在B[0]~B[k/2-1]这一部分。例如:

A={1,2,3,8}

B={,5,7}

找到第4位置上的数,此时为4。

(3)就是B[k/2-1]。例如:

A={1,2,5}

B={3,4,7}

找到第4位置上的数,此时为4。

2.当A[k/2-1]>B[k/2`-1]时,也一样。

3.为什么当A[k/2-1] == B[k/2-1]时,A[k/2-1]或B[k/2-1]就是第k位置上的数?

证明:

对于A[0]~A[k/2-2],它们全部都小于等于A[k/2-1],所以它们一定是在总数组中第k位置之前。同理,对于B[0]~B[k/2-2],它们全部都小于等于B[k/2-1],所以它们也一定是在总数组中第k位置之前。这样就有(k/2-1)+(k/2-1) = k-2个已确定的数在k位置之前。又由于A[k/2-1] == B[k/2-1],而A[k/2-1]或者B[k/2-1]是大于这k-2个数中的最小的数,故第k位置的数一定是A[k/2-1]或者B[k/2-1]。

LeetCode——4. Median of Two Sorted Arrays的更多相关文章

  1. 【算法之美】求解两个有序数组的中位数 — leetcode 4. Median of Two Sorted Arrays

    一道非常经典的题目,Median of Two Sorted Arrays.(PS:leetcode 我已经做了 190 道,欢迎围观全部题解 https://github.com/hanzichi/ ...

  2. LeetCode(3) || Median of Two Sorted Arrays

    LeetCode(3) || Median of Two Sorted Arrays 题记 之前做了3题,感觉难度一般,没想到突然来了这道比较难的,星期六花了一天的时间才做完,可见以前基础太差了. 题 ...

  3. LeetCode 4 Median of Two Sorted Arrays (两个数组的mid值)

    题目来源:https://leetcode.com/problems/median-of-two-sorted-arrays/ There are two sorted arrays nums1 an ...

  4. Leetcode 4. Median of Two Sorted Arrays(二分)

    4. Median of Two Sorted Arrays 题目链接:https://leetcode.com/problems/median-of-two-sorted-arrays/ Descr ...

  5. LeetCode 4. Median of Two Sorted Arrays & 归并排序

    Median of Two Sorted Arrays 搜索时间复杂度的时候,看到归并排序比较适合这个题目.中位数直接取即可,所以重点是排序. 再来看看治阶段,我们需要将两个已经有序的子序列合并成一个 ...

  6. 第三周 Leetcode 4. Median of Two Sorted Arrays (HARD)

    4. Median of Two Sorted Arrays 给定两个有序的整数序列.求中位数,要求复杂度为对数级别. 通常的思路,我们二分搜索中位数,对某个序列里的某个数 我们可以在对数时间内通过二 ...

  7. Leetcode 4. Median of Two Sorted Arrays(中位数+二分答案+递归)

    4. Median of Two Sorted Arrays Hard There are two sorted arrays nums1 and nums2 of size m and n resp ...

  8. LeetCode 004 Median of Two Sorted Arrays

    题目描述:Median of Two Sorted Arrays There are two sorted arrays A and B of size m and n respectively. F ...

  9. leetcode 4. Median of Two Sorted Arrays

    https://leetcode.com/problems/median-of-two-sorted-arrays/ There are two sorted arrays nums1 and num ...

  10. leetcode之 median of two sorted arrays

    这是我做的第二个leetcode题目,一开始以为和第一个一样很简单,但是做的过程中才发现这个题目非常难,给人一种“刚上战场就踩上地雷挂掉了”的感觉.后来搜了一下leetcode的难度分布表(leetc ...

随机推荐

  1. HDU2028:Lowest Common Multiple Plus

    Problem Description 求n个数的最小公倍数. Input 输入包含多个测试实例,每个测试实例的开始是一个正整数n,然后是n个正整数. Output 为每组测试数据输出它们的最小公倍数 ...

  2. ISCC的 Web——WP

    比赛已经结束了,自己做出来的题也不是很多,跟大家分享一下 第一题:比较数字大小 打开连接 在里面随意输入一个值,他会提示数字太小了 那么我们输入他允许的最大值试试 他还是提示太小了 我们知道做web‘ ...

  3. hdu4280 Island Transport 最大流

    In the vast waters far far away, there are many islands. People are living on the islands, and all t ...

  4. day 06Hadoop

    更换虚拟机以后操作的步奏1.到每一台机器上修改ip地址 ,然后修改hosts1.5 给每台机器配置免密码登录 2.修改hadoop 的配置文件,发送到每台机器上3.启动dfs start-dfs.sh ...

  5. @PropertySouce注解

    1.@ProtertySource @PropertySouce是spring3.1开始引入的基于java config的注解. 通过@PropertySource注解将properties配置文件中 ...

  6. Java解析property文件(和静哥说的,SQL执行限定时间写在xml中,增加扩展,在不改源代码基础上)

    在Java项目中一些配置参数保存在Property文件中,这样能保证不修改原代码直接修改Property文件. 简单的很,就是在java文件中读取外界的properyt配置文件 PropertyPar ...

  7. chrome自带调试工具介绍

    Chrome浏览器不仅可以调试页面.JS.请求.资源.cookie,还可以模拟手机进行调试等等,为开发者提供了很多方便,下面就介绍一下我常用到的调试技巧. 1.chrome浏览页面常用快捷键 Ctrl ...

  8. 项目中更新pip 问题。更新后还是老版本

    (venv) E:\renyuwang\venv\Scripts>python -m pip install --upgrade pipRequirement already up-to-dat ...

  9. ML(附录3)——过拟合与欠拟合

    过拟合与欠拟合 我们希望机器学习得到好的模型,该模型能够从训练样本中找到一个能够适应潜在样本的普遍规律.然而,如果机器学习学的“太好”了,以至把样本的自身特点当作潜在样本的一般特性,这就使得模型的泛化 ...

  10. COLUMN_FORMAT 的值:FIXED、DYNAMIC、DEFAULT 的区别(待补充)

    参考===MySQL 建表语句 create table 中的列定义: column_definition: data_type [NOT NULL | NULL] [DEFAULT default_ ...