两个排序数组的中位数
2012-09-01 16:08
267 查看
求两个排序数组中位数,这道题是很有意思的一道题目,算法导论中9.3-8题,这题必须在O(logn)的时间复杂度求解,否则肯定悲剧。。。
这题有个关键的条件,那就是这两个数组长度相等
思路如下:
数组A:1, 3, 5, 7, 9
数组B:2, 4, 6, 8, 10
首先取二者的中位数,在O(1)时间复杂度内求出,那么数组A的midValue = 5,数组B的midValue = 6,则较小的元素midValue的左边所有元素,也就是midPosition个,和较大元素右边的所有元素,都要去掉,由于去掉的元素占所有元素的一半,所以复杂度为O(logn)。只需要移动begin和end的position即可。
第一轮:
数组A:1, 3, 5, 7, 9
数组B:2, 4, 6, 8, 10
第二轮:
数组A:1, 3, 5, 7, 9
数组B:2, 4, 6, 8, 10
只剩下两个元素,由于这种情形下midValue始终是偏左的元素,因此循环无法退出,所以满足lhsBegin == lhsEnd - 1和 rhsBegin == rhsEnd - 1时,循环退出!
如果两个
上述情形是当数组中有两个及以上元素才成立,必须考虑两个数组只存在一个元素的情形,那么直接取较小的元素即可!
当两个数组长度不等时,这种情形比较复杂:
情形一:
当一个数组长度为1,另一个数组长度为奇数的情形:
例如A:1 B:2, 3, 4
这个时候的比较策略是,A的元素同B的中位数进行比较,若A的元素较小,则中位数一定是max(A[0], B[mid - 1]),若A的元素较大,则中位数一定是B[mid]
情形二:
例如A: 1 B:2, 3, 4, 5
这个时候的比较策略是,A的元素同B的中位数进行比较,若A的元素较小,则中位数一定是B[mid],若A的元素较大,则中位数一定是min(A[0], B[mid + 1])
下面引申一种情形:
当一个数组长度为2,另一个数组长度为奇数时:
例如A:1, 3 B:2, 4, 6
如果A[mid] < B[mid],那么A[mid]肯定不会是中位数,由于A只有两个元素,那么A[1]将会是中位数的候选对象,那么此情形退化为上面第一种情形
下面对一个例子进行分析:
数组A:1, 3, 5, 7, 9, 11
数组B:2, 4, 6, 8
第一轮:
数组A:1, 3, 5, 7, 9, 11
数组B:2, 4, 6, 8
第二轮:
数组A:1,3, 5, 7, 9, 11
数组B:2, 4, 6, 8
进行到这里,那么删除将不能再进行下去,可以肯定的是其中一个数组只有可能是1个或者2个元素,适用于上面分析的情形。
说下上面的删除策略:
即删除较小的左边元素和较大的右边元素较小的个数。
删除策略进行不下去还有一种情况,那就是rightB为0,当rightB为0时,B的元素只有可能是一个,因为B有多个元素时,B的中位数右边肯定还存在元素。
不难写出代码如下:
(rhsEnd - rhsBegin) & 0x01成立表示有偶数个数组,不信用笔画一画下标index~~~
关于中位数的其他求法,罗列如下:
1,题目
有两个数组,均已经按升序排列好,编程序计算这两个数组的中位数
要求:要求时间复杂度O(lgn) 空间复杂度O(1)
例子:
数组A:{1,4,6,7,9} B{2,3,5,8} 两数组合并后{1,2,3,4,5,6,7,8,9} 中位数就是中间的那个数: 5
2,方法:
对两个数组分别二分找解
对每个元素可以O(1)判断它在另外一个数组应该所在的位置,从而可以判断选大了还是小了,继续二分直到找到解为止
先二分第一个数组找解,可选区域为[0,4]。选中A[2]=6,6前面有两个元素,如果将其插入第二个数组,那么6如果是解,则必须前面有4-2=2个元素(排第三位)
通过比较前后元素发现6放在B中的第三位明显大了,需要更小的元素,这样就将下一次二分的区域减了一半.
在A和B中重复这个过程,直到找到解为止
3,3,扩展
M个长度为N的排序好的数组 求中位数
目前有两个比较好的算法
算法一
(1)取所有数组的最小值和最大值,求出全体的最大值和最小值min max 然后取m=(min+max)/2
(2)用m在所有数组中搜索 找到比m小的数的个数n1 若n1大于M*N/2 则mid=(min+mid)/2 否则mid=(mid+max)/2
(3)重复上述搜索一共循环log(max-min)次
算法二
(1)取所有数组的中值排序
弃掉这个最大值所在数组的后半部分和最小值所在数组的前半部分 再将这两个只剩一半的数组合并 O(N)
(2)从合并后的数组中找出中值插入到之前的中值排序数组里 o(logN)
(3)重复(1)(2),每次循环可以舍掉一个数组,一共需要循环M次
思想与本文实质相同!
这题有个关键的条件,那就是这两个数组长度相等
思路如下:
数组A:1, 3, 5, 7, 9
数组B:2, 4, 6, 8, 10
首先取二者的中位数,在O(1)时间复杂度内求出,那么数组A的midValue = 5,数组B的midValue = 6,则较小的元素midValue的左边所有元素,也就是midPosition个,和较大元素右边的所有元素,都要去掉,由于去掉的元素占所有元素的一半,所以复杂度为O(logn)。只需要移动begin和end的position即可。
第一轮:
数组A:1, 3, 5, 7, 9
数组B:2, 4, 6, 8, 10
第二轮:
数组A:1, 3, 5, 7, 9
数组B:2, 4, 6, 8, 10
只剩下两个元素,由于这种情形下midValue始终是偏左的元素,因此循环无法退出,所以满足lhsBegin == lhsEnd - 1和 rhsBegin == rhsEnd - 1时,循环退出!
如果两个
上述情形是当数组中有两个及以上元素才成立,必须考虑两个数组只存在一个元素的情形,那么直接取较小的元素即可!
#include <iostream> using namespace std; int findMidValue(int *lhs, int *rhs, int size) { if (lhs == NULL || rhs == NULL || size <= 0) return -1; int lhsBegin = 0; int rhsBegin = 0; int lhsEnd = size - 1; int rhsEnd = size - 1; int result; while ((lhsBegin < lhsEnd && rhsBegin < rhsEnd)) { if ((lhsBegin == lhsEnd - 1 && rhsBegin == rhsEnd - 1)) break; int lhsMid = (lhsBegin + lhsEnd) >> 1; int rhsMid = (rhsBegin + rhsEnd) >> 1; if (lhs[lhsMid] == rhs[rhsMid]) { result = lhs[lhsMid]; break; } else if (lhs[lhsMid] < rhs[rhsMid]) { lhsBegin = lhsMid; rhsEnd = rhsMid; } else { lhsEnd = lhsMid; rhsBegin = rhsMid; } } if (lhsBegin == lhsEnd && rhsBegin == rhsEnd) result = min(lhs[lhsBegin], rhs[rhsBegin]); else if (lhsBegin == lhsEnd - 1 && rhsBegin == rhsEnd - 1) { if (lhs[lhsBegin] < rhs[rhsBegin]) { result = min(lhs[lhsEnd], rhs[rhsBegin]); } else { result = min(lhs[lhsBegin], rhs[rhsEnd]); } } return result; } void main() { int lhs[] = {1, 2, 3, 4}; int rhs[] = {0, 2, 3, 4}; const int size = sizeof lhs / sizeof *lhs; int result = findMidValue(lhs, rhs, size); cout << "mid value = " << result << endl; }
当两个数组长度不等时,这种情形比较复杂:
情形一:
当一个数组长度为1,另一个数组长度为奇数的情形:
例如A:1 B:2, 3, 4
这个时候的比较策略是,A的元素同B的中位数进行比较,若A的元素较小,则中位数一定是max(A[0], B[mid - 1]),若A的元素较大,则中位数一定是B[mid]
情形二:
例如A: 1 B:2, 3, 4, 5
这个时候的比较策略是,A的元素同B的中位数进行比较,若A的元素较小,则中位数一定是B[mid],若A的元素较大,则中位数一定是min(A[0], B[mid + 1])
下面引申一种情形:
当一个数组长度为2,另一个数组长度为奇数时:
例如A:1, 3 B:2, 4, 6
如果A[mid] < B[mid],那么A[mid]肯定不会是中位数,由于A只有两个元素,那么A[1]将会是中位数的候选对象,那么此情形退化为上面第一种情形
下面对一个例子进行分析:
数组A:1, 3, 5, 7, 9, 11
数组B:2, 4, 6, 8
第一轮:
数组A:1, 3, 5, 7, 9, 11
数组B:2, 4, 6, 8
第二轮:
数组A:1,3, 5, 7, 9, 11
数组B:2, 4, 6, 8
进行到这里,那么删除将不能再进行下去,可以肯定的是其中一个数组只有可能是1个或者2个元素,适用于上面分析的情形。
说下上面的删除策略:
if (A[amid] < B[bmid]) { int leftA = amid; int rightB = bend - amid - 1; int minValue = min(leftA, rightB); abegin = leftA + minValue; bend = bend - minValue; }
即删除较小的左边元素和较大的右边元素较小的个数。
删除策略进行不下去还有一种情况,那就是rightB为0,当rightB为0时,B的元素只有可能是一个,因为B有多个元素时,B的中位数右边肯定还存在元素。
不难写出代码如下:
#include <iostream> using namespace std; int findMidValue(int *lhs, int sizeLhs, int *rhs, int sizeRhs) { if (lhs == NULL || rhs == NULL || sizeLhs <= 0 || sizeRhs <= 0) return -1; int lhsBegin = 0; int rhsBegin = 0; int lhsEnd = sizeLhs - 1; int rhsEnd = sizeRhs - 1; int result; bool flagLhs = false; while ((lhsBegin <= lhsEnd && rhsBegin <= rhsEnd)) { int lhsMid = (lhsBegin + lhsEnd) >> 1; int rhsMid = (rhsBegin + rhsEnd) >> 1; int lhsLeft, lhsRight; int rhsLeft, rhsRight; if (lhs[lhsMid] < rhs[rhsMid]) { lhsLeft = lhsMid - lhsBegin; rhsRight = rhsEnd - rhsMid; int minValue = min(lhsLeft, rhsRight); if (minValue == 0) { if (lhsLeft == 0 && rhsRight == 0) { if ((lhsEnd - lhsBegin) & 0x01) { result = max(lhs[lhsMid], rhs[rhsBegin]); } else { result = min(lhs[lhsMid], rhs[rhsBegin]); } } else if (lhsLeft == 0) { //lhs has one elem if (lhsBegin == lhsEnd) { if ((rhsEnd - rhsBegin) & 0x01) { if (lhs[lhsBegin] < rhs[rhsMid]) result = rhs[rhsMid]; else result = min(lhs[lhsBegin], rhs[rhsMid + 1]); } else { if (lhs[lhsBegin] < rhs[rhsMid]) result = max(lhs[lhsBegin], rhs[rhsMid - 1]); else result = rhs[rhsMid]; } } //lhs two elems else { if (lhs[lhsEnd] <= rhs[rhsEnd]) { rhsEnd = rhsEnd - 1; if ((rhsEnd - rhsBegin) & 0x01) { if (lhs[lhsEnd] <= rhs[(rhsBegin + rhsEnd) >> 1]) result = rhs[(rhsBegin + rhsEnd) >> 1]; else result = min(lhs[lhsEnd], rhs[(rhsBegin + rhsEnd) / 2 + 1]); } else { if (lhs[lhsEnd] <= rhs[rhsMid]) result = max(lhs[lhsEnd], rhs[rhsMid]); else result = rhs[rhsMid]; } } else { result = rhs[rhsMid]; } } } else { //rsh must have one elem if ((lhsEnd - lhsBegin) & 0x01) { if (rhs[rhsBegin] < lhs[lhsMid]) result = lhs[lhsMid]; else result = min(rhs[rhsBegin], lhs[lhsMid + 1]); } else { if (rhs[rhsBegin] < lhs[lhsMid]) result = max(rhs[rhsBegin], lhs[lhsMid - 1]); else result = lhs[lhsMid]; } } break; } lhsBegin = lhsBegin + minValue; rhsEnd = rhsEnd - minValue; } else { lhsRight = lhsEnd - lhsMid; rhsLeft = rhsMid - rhsBegin; int minValue = min(lhsRight, rhsLeft); if (minValue == 0) { if (lhsRight == 0 && rhsLeft == 0) { if ((rhsEnd - rhsBegin) & 0x01) { result = max(lhs[lhsBegin], rhs[rhsMid]); } else { result = min(lhs[lhsBegin], rhs[rhsMid]); } } else if (rhsLeft == 0) { //rhs has one elem if (rhsBegin == rhsEnd) { if ((lhsEnd - lhsBegin) & 0x01) { if (rhs[rhsBegin] < lhs[lhsMid]) result = lhs[lhsMid]; else result = min(rhs[rhsBegin], lhs[lhsMid + 1]); } else { if (rhs[rhsBegin] < lhs[lhsMid]) result = max(rhs[rhsBegin], lhs[lhsMid - 1]); else result = lhs[lhsMid]; } } //rhs two elems else { if (rhs[rhsEnd] <= lhs[lhsEnd]) { lhsEnd = lhsEnd - 1; if ((lhsEnd - lhsBegin) & 0x01) { if (rhs[rhsEnd] <= lhs[(lhsBegin + lhsEnd) >> 1]) result = lhs[(lhsBegin + lhsEnd) >> 1]; else result = min(rhs[rhsEnd], lhs[(lhsBegin + lhsEnd) / 2 + 1]); } else { if (rhs[rhsEnd] <= lhs[(lhsBegin + lhsEnd) >> 1]) result = max(rhs[rhsEnd], lhs[(lhsBegin + lhsEnd) / 2 - 1]); else result = lhs[(lhsBegin + lhsEnd) >> 1]; } } else { result = lhs[lhsMid]; } } } else { //lsh must have one elem if ((rhsEnd - rhsBegin) & 0x01) { if (lhs[lhsBegin] < rhs[rhsMid]) result = rhs[rhsMid]; else result = min(lhs[lhsBegin], rhs[rhsMid + 1]); } else { if (lhs[lhsBegin] < rhs[rhsMid]) result = max(lhs[lhsBegin], rhs[rhsMid - 1]); else result = rhs[rhsMid]; } } break; } lhsEnd = lhsEnd - minValue; rhsBegin = rhsBegin + minValue; } } return result; } void main() { int lhs[] = {1}; int rhs[] = {2, 3, 4}; const int size1 = sizeof lhs / sizeof *lhs; const int size2 = sizeof rhs / sizeof *rhs; int result = findMidValue(lhs, size1, rhs, size2); cout << "mid value = " << result << endl; }
(rhsEnd - rhsBegin) & 0x01成立表示有偶数个数组,不信用笔画一画下标index~~~
关于中位数的其他求法,罗列如下:
1,题目
有两个数组,均已经按升序排列好,编程序计算这两个数组的中位数
要求:要求时间复杂度O(lgn) 空间复杂度O(1)
例子:
数组A:{1,4,6,7,9} B{2,3,5,8} 两数组合并后{1,2,3,4,5,6,7,8,9} 中位数就是中间的那个数: 5
2,方法:
对两个数组分别二分找解
对每个元素可以O(1)判断它在另外一个数组应该所在的位置,从而可以判断选大了还是小了,继续二分直到找到解为止
先二分第一个数组找解,可选区域为[0,4]。选中A[2]=6,6前面有两个元素,如果将其插入第二个数组,那么6如果是解,则必须前面有4-2=2个元素(排第三位)
通过比较前后元素发现6放在B中的第三位明显大了,需要更小的元素,这样就将下一次二分的区域减了一半.
在A和B中重复这个过程,直到找到解为止
3,3,扩展
M个长度为N的排序好的数组 求中位数
目前有两个比较好的算法
算法一
(1)取所有数组的最小值和最大值,求出全体的最大值和最小值min max 然后取m=(min+max)/2
(2)用m在所有数组中搜索 找到比m小的数的个数n1 若n1大于M*N/2 则mid=(min+mid)/2 否则mid=(mid+max)/2
(3)重复上述搜索一共循环log(max-min)次
算法二
(1)取所有数组的中值排序
弃掉这个最大值所在数组的后半部分和最小值所在数组的前半部分 再将这两个只剩一半的数组合并 O(N)
(2)从合并后的数组中找出中值插入到之前的中值排序数组里 o(logN)
(3)重复(1)(2),每次循环可以舍掉一个数组,一共需要循环M次
思想与本文实质相同!
相关文章推荐
- [各种面试题] 两个排序数组的中位数
- 两个排序数组的中位数
- 两个排序数组的中位数
- 求两个大小为n的已排序数组的中位数
- lintcode, 两个排序数组的中位数
- 找出两个已排序且同序大小相等的数组的中位数C语言
- 两个排序数组的中位数
- 两个排序数组的中位数
- leetcode04 Median of Two Sorted Arrays 两个已排序数组的中位数
- 两个排序数组的中位数
- Median of Two Sorted Arrays 求出两个已排序数组的中位数
- 求两个排序数组的中位数
- Median of Two Sorted Arrays 求两个排序数组中位数 题解翻译
- 从0打卡leetcode之day 5 ---两个排序数组的中位数
- [leetcode] Median of Two Sorted Arrays 两个排序数组找出第k个数,或者中位数
- LeetCodeLeetCode 两个排序数组的中位数问题
- 【LeetCode】4. 两个排序数组的中位数 结题报告 (C++)
- 两个排序数组的中位数
- leetcode解题之4. Median of Two Sorted Arrays java (两个排序数组的中位数)
- 求两个或多个已排序数组的中位数