一.题目链接: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. GitHub使用教程、注册与安装

    GitHub注册与安装 本文提供全流程,中文翻译.Chinar坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请调整网页缩放比例至200%) 1 进入GitHub官网:http ...

  2. day 014 python 内置函数

    1. lamda匿名函数2. sorted()3. filter()4. map()5. 递归函数 一 匿名函数(lambda)   函数名=  lambda 参数: 返回值 简单算法 a+b 常规算 ...

  3. Project Euler 54

    #include<bits/stdc++.h> using namespace std; ]; ]; ; map<char,int> mp; //map<char,cha ...

  4. Red Hat OpenShift

    Core Concepts 基本概念 Master Master监控其他Node和Pod 提供Web Console Node Node是k8s的工作机器 Node中运行Pod,pod可以在node中 ...

  5. maven settings.xml配置优化

    <?xml version="1.0" encoding="UTF-8"?> <settings> <localRepositor ...

  6. 【mysql】索引原理-MySQL索引原理以及查询优化

    转载:https://www.cnblogs.com/bypp/p/7755307.html 一.介绍 1.什么是索引? 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性 ...

  7. linux之数据备份

    第一种方法:tar备份 [root@bogon ~]# cat bp/linux.txt no centos [root@bogon ~]# tar cvf bp.tar bp //打包bp目录 bp ...

  8. Creating Node.js Command Line Utilities to Improve Your Workflow

    转自:https://developer.telerik.com/featured/creating-node-js-command-line-utilities-improve-workflow/ ...

  9. Debug outlook add-in (office.js) 小技巧

    这几天在使用office.js 做outlook add-in的时候出现了一个问题: 不知道运行时去调试. 这里给大家介绍两个调试add-in 的方法. office365 其他软件 add-ins ...

  10. Autofac学习之三种生命周期:InstancePerLifetimeScope、SingleInstance、InstancePerDependency 【转载】

    InstancePerLifetimeScope:同一个Lifetime生成的对象是同一个实例 SingleInstance:单例模式,每次调用,都会使用同一个实例化的对象:每次都用同一个对象: In ...