4-median of two sorted arrays
2017-09-13 00:09
453 查看
难度:hard
类别:divide and conquer
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
nums2 = [2]
The median is 2.0
首先,最基本的想法是将两个数组进行合并然后进行排序,直接取中位数即可,这样实现非常简单,但是时间复杂度超过了log(m+n)。
基本的算法思路:
(1)将两个数组分别进行拆分,假设两个数组分别为nums1和nums2。对于nums1,进行对半分,可以得到:
i = (imin + imax)/2,最开始的时候imin=0, imax = m,
nums1的左半部分为:nums1[imin]、nums1[1]……nums1[i-1]
nums1的右半部分为:nums1[i]、nums1[i+1]……nums1[imax];
同理,j = (m+n+1)/2 - i;
j这样取值刚好保证了nums1的左半部分和nums2的左半部分相加的长度与nums1的右半部分和nums2的右半部分相加的长度相等。从而方便后面直接得到中位数。
(2)对于nums1Left + nums2Left和nums1Right + nums2Right,可能有下面的三种情况。
nums1[i-1] <= nums2[j] && nums2[j-1] <= nums1[i]
此时得到的i刚好满足nums1Left + nums2Left和nums1Right + nums2Right为两个数组合并后的左右两半部分,从而可以直接得到中位数。但是,在得到中位数的时候,需要注意下标越界的问题,因为涉及到i-1、j-1以及i和j
nums1[i-1] > nums2[j]
因为要得到nums1[i-1] <= nums2[j],所以需要将i减少,所以接下来[imin, i-1]内寻找合适的下标
nums2[j-1] > nums1[i]
同样地,因为要得到nums2[j-1] <= nums1[i],所以要讲i增加,所以接下来要在[i+1, imax]中寻找合适的下标,在2和3两种情况下实现了分治,将大问题分解为小问题,从而减少了实现的时间。
注:在实现的过程中有一些小问题需要注意,包括数组下标越界的问题以及当某个数组的元素个数为0时应该如何特殊处理等问题的考虑。另外在分解子问题的时候,既可以将imin转换为imin+1.也可以转换为i + 1;但是转换为i+1可以减少运行的时间。
题目描述:
分金币问题:
圆桌旁边坐着n个人,每人有一定数量的金币,金币总数能被n整除。每个人可以给他左右相邻的人一些金币,最终使得每个人的金币数目相等。你的任务是求出被转手的金币数量的最小值。按逆时针方向。
样例输入:
3
100 100 100
样例输出:0
样例输入:
4 1 2 5 4
样例输出:4
题目分析:
假设有4个人,按顺序编号为1,2,3,4。假设1号给2号3枚金币,然后2号给1号5枚金币,相当于2号给1号2枚金币,而1号没有给2号。设x2表示的是2号给1号的金币数目,同理,有x1,x3,x4。
M表示的是最终每个人得到的平均金币的数目,即sum/n; A表示的是每个人初始拥有的金币的数目。
对于第一个人:A1 - x1 + x2 = M 推出 x2 = M - A1 + x1 = x1 - C1(C1=A1 - M)
对于第二个人:A2 - x2 + x3 = M 推出 x3 = M - A2 + x2 = 2M - A1 - A2 + x1 = x1 - C2
对于第三个人:A3 - x3 + x4 = M 推出 x4 = x1 - C3
……
要使得所有的xi的绝对值之和尽量小,即|x1| + |x1-C1| + |x2-C2| + … + |x1-Cn-1|要最小,这实际上就是中位数的问题。
代码实现:
类别:divide and conquer
1.问题描述
There are two sorted arrays nums1 and nums2 of size m and n respectively.Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
2.测试样例
nums1 = [1, 3]nums2 = [2]
The median is 2.0
3.问题分析和理解
这道题用到了分治的思想,将大问题分解为小问题,然后进行实现,从而减少了时间。首先,最基本的想法是将两个数组进行合并然后进行排序,直接取中位数即可,这样实现非常简单,但是时间复杂度超过了log(m+n)。
基本的算法思路:
(1)将两个数组分别进行拆分,假设两个数组分别为nums1和nums2。对于nums1,进行对半分,可以得到:
i = (imin + imax)/2,最开始的时候imin=0, imax = m,
nums1的左半部分为:nums1[imin]、nums1[1]……nums1[i-1]
nums1的右半部分为:nums1[i]、nums1[i+1]……nums1[imax];
同理,j = (m+n+1)/2 - i;
j这样取值刚好保证了nums1的左半部分和nums2的左半部分相加的长度与nums1的右半部分和nums2的右半部分相加的长度相等。从而方便后面直接得到中位数。
(2)对于nums1Left + nums2Left和nums1Right + nums2Right,可能有下面的三种情况。
nums1[i-1] <= nums2[j] && nums2[j-1] <= nums1[i]
此时得到的i刚好满足nums1Left + nums2Left和nums1Right + nums2Right为两个数组合并后的左右两半部分,从而可以直接得到中位数。但是,在得到中位数的时候,需要注意下标越界的问题,因为涉及到i-1、j-1以及i和j
nums1[i-1] > nums2[j]
因为要得到nums1[i-1] <= nums2[j],所以需要将i减少,所以接下来[imin, i-1]内寻找合适的下标
nums2[j-1] > nums1[i]
同样地,因为要得到nums2[j-1] <= nums1[i],所以要讲i增加,所以接下来要在[i+1, imax]中寻找合适的下标,在2和3两种情况下实现了分治,将大问题分解为小问题,从而减少了实现的时间。
4.代码实现
(1)第一种实现方法:采用分治的思想
class Solution { public: double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { int m = nums1.size(); int n = nums2.size(); if (m > n) { vector<int> tmp = nums1; nums1 = nums2; nums2 = tmp; } m = nums1.size(); n = nums2.size(); if (n == 0) { return 0.0; } if (m == 0) { if ((n % 2) == 0) { int medianLeft = nums2[n/2 - 1]; int medianRight = nums2[n/2]; return (medianLeft + medianRight)/2.0; } else { return nums2[n/2]; } } int imin = 0, imax = m; int i, j; while (imin <= imax) { i = (imin + imax)/2; j = (m+n+1)/2 - i; if (nums1[i-1] > nums2[j] && i > imin) { // here divide the problem into subproblem imax = i - 1; } else if (nums2[j-1] > nums1[i] && i < imax) { imin = i + 1; } else { // find the index i that satisfies, that is, nums1[i-1] <= nums2[j] && nums2[j-1] <= nums1[i] int maxOne, minOne; // 注意下标越界问题 if (i == 0 && j == 0) maxOne = 0; else if (i == 0) maxOne = nums2[j-1]; else if (j == 0) maxOne = nums1[i-1]; else maxOne = nums1[i-1] > nums2[j-1] ? nums1[i - 1] : nums2[j - 1]; if (i == m && j == n) minOne = 0; else if (i == m) minOne = nums2[j]; else if (j == n) minOne = nums1[i]; else minOne = nums1[i] < nums2[j] ? nums1[i] : nums2[j]; int isOdd = (m+n)%2; if (isOdd == 0) { return (maxOne + minOne)/2.0; } else { return maxOne; } } } return 0.0; } };
(2)第二种实现方法:使用队列,时间复杂度同样不高,算法的实现相对于第一种方法要简单些
// 在时间的限制上同样是满足的,在leetcode上亲测通过 // 采用队列,每次比较对头的两个数字,将两个数组整合为一个排序的大数组,然后直接得到中位数 #include <iostream> #include <vector> #include <queue> using namespace std; // return the double value but not the integer double findMedian(vector<int> nums1, vector<int> nums2); int main() { int n, m, data; vector<int> nums1, nums2; cin >> m; cin >> n; for (int i = 0; i < m; ++i) { cin >> data; nums1.push_back(data); } for (int i = 0; i < n; ++i) { cin >> data; nums2.push_back(data); } cout << findMedian(nums1, nums2) << endl; return 0; } double findMedian(vector<int> nums1, vector<int> nums2) { int m = nums1.size(); int n = nums2.size(); if (m > n) { vector<int> tmp = nums1; nums1 = nums2; nums2 = tmp; } m = nums1.size(); n = nums2.size(); if (n == 0) { return 0.0; } if (m == 0) { if ((n % 2) == 0) { int medianLeft = nums2[n/2 - 1]; int medianRight = nums2[n/2]; return (medianLeft + medianRight)/2.0; } else { return nums2[n/2]; } } queue<int> data1, data2; for (int i = 0; i < m; ++i) { data1.push(nums1[i]); } for (int i = 0; i < n; ++i) { data2.push(nums2[i]); } vector<int> result; int i = 0, j = 0; while (!(data1.empty() && data2.empty())) { if (!data1.empty() && !data2.empty()) { if (data1.front() < data2.front()) { result.push_back(data1.front()); data1.pop(); } else if (data1.front() > data2.front()) { result.push_back(data2.front()); data2.pop(); } else { result.push_back(data2.front()); result.push_back(data1.front()); data1.pop(); data2.pop(); } } else if (data1.empty() && !data2.empty()) { while (!data2.empty()) { result.push_back(data2.front()); data2.pop(); } } else { while (!data1.empty()) { result.push_back(data1.front()); data1.pop(); } } } int len = result.size(); if (len % 2 == 1) { return result[len/2]; } else { return (result[len/2 - 1] + result[len/2])/2.0; } return 0.0; }
注:在实现的过程中有一些小问题需要注意,包括数组下标越界的问题以及当某个数组的元素个数为0时应该如何特殊处理等问题的考虑。另外在分解子问题的时候,既可以将imin转换为imin+1.也可以转换为i + 1;但是转换为i+1可以减少运行的时间。
5.同类问题拓展
下面的题目同样是中位数的问题,但是不是使用分治算法实现的。题目描述:
分金币问题:
圆桌旁边坐着n个人,每人有一定数量的金币,金币总数能被n整除。每个人可以给他左右相邻的人一些金币,最终使得每个人的金币数目相等。你的任务是求出被转手的金币数量的最小值。按逆时针方向。
样例输入:
3
100 100 100
样例输出:0
样例输入:
4 1 2 5 4
样例输出:4
题目分析:
假设有4个人,按顺序编号为1,2,3,4。假设1号给2号3枚金币,然后2号给1号5枚金币,相当于2号给1号2枚金币,而1号没有给2号。设x2表示的是2号给1号的金币数目,同理,有x1,x3,x4。
M表示的是最终每个人得到的平均金币的数目,即sum/n; A表示的是每个人初始拥有的金币的数目。
对于第一个人:A1 - x1 + x2 = M 推出 x2 = M - A1 + x1 = x1 - C1(C1=A1 - M)
对于第二个人:A2 - x2 + x3 = M 推出 x3 = M - A2 + x2 = 2M - A1 - A2 + x1 = x1 - C2
对于第三个人:A3 - x3 + x4 = M 推出 x4 = x1 - C3
……
要使得所有的xi的绝对值之和尽量小,即|x1| + |x1-C1| + |x2-C2| + … + |x1-Cn-1|要最小,这实际上就是中位数的问题。
代码实现:
// 实际上也是中位数问题 #include <iostream> #include <algorithm> #include <stdio.h> using namespace std; const int maxn = 1000001; long long A[maxn], C[maxn]; long long total, average; int main() { int n; while (scanf("%d", &n) == 1) { total = 0; for (int i = 1; i <= n; i++) { scanf("%lld", &A[i]); total += A[i]; } average = total / n; C[0] = 0; for (int i = 1; i < n; ++i) { C[i] = C[i-1] + A[i] - average; } sort(C, C+n); long long median = C[n/2]; long long result = 0; for (int i = 0; i < n; ++i) { result += abs(median - C[i]); } cout << result << endl; } return 0; }
总结:
中位数在很多算法题目中都有巧妙的应用,分治算法思想最重要的是找到如何将大问题进行分解的方法,这道题目因为题目要求时间为log(m+n),所以可以从这个角度入手,考虑二分法,从而得到将两个数组分别进行半分然后合并的方法。相关文章推荐
- leetcode Median of Two Sorted Arrays
- [Leetcode] 4 - Median of Two Sorted Arrays
- Leetcode: Median of Two Sorted Arrays 理解分析
- LeetCode 4 Median of Two Sorted Arrays
- [LeetCode] Median of Two Sorted Arrays
- Leetcode-median of two sorted arrays
- Median of Two Sorted Arrays-LeetCode
- 2.1.5 Median of Two Sorted Arrays
- **Leetcode_median-of-two-sorted-arrays (c++ and python version)
- 4 Median of Two Sorted Arrays
- LeetCode之Median of Two Sorted Arrays
- Median of Two Sorted Arrays
- LeetCode Median of Two Sorted Arrays (DFS)
- LeetCode 4 Median of Two Sorted Arrays
- [LeetCode][4]Median of Two Sorted Arrays解析 -Java实现
- LeetCode #4 Median of Two Sorted Arrays C# Solution
- [leetcode] Median of Two Sorted Arrays
- Median of Two Sorted Arrays
- LeetCode 4 Median of Two Sorted Arrays (两个数组的mid值)
- leetcode Median of Two Sorted Arrays