【算法】【分治】两个排序数组的中位数

转自;http://www.cnblogs.com/bigwangdi/p/3310711.html

求两个排序数组中位数,这道题是很有意思的一道题目,算法导论中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. #include <iostream>  
  2.   
  3. using namespace std;  
  4.   
  5. int findMidValue(int *lhs, int *rhs, int size)  
  6. {  
  7.     if (lhs == NULL || rhs == NULL || size <= 0)  
  8.         return -1;  
  9.   
  10.     int lhsBegin = 0;  
  11.     int rhsBegin = 0;  
  12.     int lhsEnd = size - 1;  
  13.     int rhsEnd = size - 1;  
  14.     int result;  
  15.   
  16.     while ((lhsBegin < lhsEnd && rhsBegin < rhsEnd))  
  17.     {  
  18.         if ((lhsBegin == lhsEnd - 1 && rhsBegin == rhsEnd - 1))  
  19.             break;  
  20.         int lhsMid = (lhsBegin + lhsEnd) >> 1;  
  21.         int rhsMid = (rhsBegin + rhsEnd) >> 1;  
  22.           
  23.         if (lhs[lhsMid] == rhs[rhsMid])  
  24.         {  
  25.             result = lhs[lhsMid];  
  26.             break;  
  27.         }  
  28.         else if (lhs[lhsMid] < rhs[rhsMid])  
  29.         {  
  30.             lhsBegin = lhsMid;  
  31.             rhsEnd = rhsMid;  
  32.         } else  
  33.         {  
  34.             lhsEnd = lhsMid;  
  35.             rhsBegin = rhsMid;  
  36.         }  
  37.     }  
  38.   
  39.     if (lhsBegin == lhsEnd && rhsBegin == rhsEnd)  
  40.         result = min(lhs[lhsBegin], rhs[rhsBegin]);  
  41.     else if (lhsBegin == lhsEnd - 1 && rhsBegin == rhsEnd - 1)  
  42.     {  
  43.         if (lhs[lhsBegin] < rhs[rhsBegin])  
  44.         {  
  45.             result = min(lhs[lhsEnd], rhs[rhsBegin]);  
  46.         }  
  47.         else  
  48.         {  
  49.             result = min(lhs[lhsBegin], rhs[rhsEnd]);  
  50.         }  
  51.     }  
  52.   
  53.     return result;  
  54. }  
  55.   
  56. void main()  
  57. {  
  58.     int lhs[] = {1, 2, 3, 4};  
  59.     int rhs[] = {0, 2, 3, 4};  
  60.     const int size = sizeof lhs / sizeof *lhs;  
  61.     int result = findMidValue(lhs, rhs, size);  
  62.     cout << "mid value = " << result << endl;  
  63. }  

 

当两个数组长度不等时,这种情形比较复杂:

情形一:

当一个数组长度为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个元素,适用于上面分析的情形。

说下上面的删除策略:

  1. if (A[amid] < B[bmid])  
  2. {  
  3.     int leftA = amid;  
  4.     int rightB = bend - amid - 1;  
  5.     int minValue = min(leftA, rightB);  
  6.     abegin = leftA + minValue;  
  7.     bend = bend - minValue;  
  8. }  

 

即删除较小的左边元素和较大的右边元素较小的个数。

删除策略进行不下去还有一种情况,那就是rightB为0,当rightB为0时,B的元素只有可能是一个,因为B有多个元素时,B的中位数右边肯定还存在元素。

不难写出代码如下:

  1. #include <iostream>  
  2.   
  3. using namespace std;  
  4.   
  5. int findMidValue(int *lhs, int sizeLhs, int *rhs, int sizeRhs)  
  6. {  
  7.     if (lhs == NULL || rhs == NULL || sizeLhs <= 0 || sizeRhs <= 0)  
  8.         return -1;  
  9.   
  10.     int lhsBegin = 0;  
  11.     int rhsBegin = 0;  
  12.     int lhsEnd = sizeLhs - 1;  
  13.     int rhsEnd = sizeRhs - 1;  
  14.     int result;  
  15.     bool flagLhs = false;  
  16.   
  17.     while ((lhsBegin <= lhsEnd && rhsBegin <= rhsEnd))  
  18.     {  
  19.         int lhsMid = (lhsBegin + lhsEnd) >> 1;  
  20.         int rhsMid = (rhsBegin + rhsEnd) >> 1;  
  21.         int lhsLeft, lhsRight;  
  22.         int rhsLeft, rhsRight;  
  23.           
  24.         if (lhs[lhsMid] < rhs[rhsMid])  
  25.         {  
  26.             lhsLeft = lhsMid - lhsBegin;  
  27.             rhsRight = rhsEnd - rhsMid;  
  28.             int minValue = min(lhsLeft, rhsRight);  
  29.             if (minValue == 0)  
  30.             {  
  31.                 if (lhsLeft == 0 && rhsRight == 0)  
  32.                 {  
  33.                     if ((lhsEnd - lhsBegin) & 0x01)  
  34.                     {  
  35.                         result = max(lhs[lhsMid], rhs[rhsBegin]);  
  36.                     }  
  37.                     else  
  38.                     {  
  39.                         result = min(lhs[lhsMid], rhs[rhsBegin]);  
  40.                     }  
  41.                 }  
  42.                 else if (lhsLeft == 0)  
  43.                 {  
  44.                     //lhs has one elem  
  45.                     if (lhsBegin == lhsEnd)  
  46.                     {  
  47.                         if ((rhsEnd - rhsBegin) & 0x01)  
  48.                         {  
  49.                             if (lhs[lhsBegin] < rhs[rhsMid])  
  50.                                 result = rhs[rhsMid];  
  51.                             else  
  52.                                 result = min(lhs[lhsBegin], rhs[rhsMid + 1]);  
  53.                         }  
  54.                         else  
  55.                         {  
  56.                             if (lhs[lhsBegin] < rhs[rhsMid])  
  57.                                 result = max(lhs[lhsBegin], rhs[rhsMid - 1]);  
  58.                             else  
  59.                                 result = rhs[rhsMid];  
  60.                         }  
  61.                     }  
  62.                     //lhs two elems  
  63.                     else  
  64.                     {  
  65.                         if (lhs[lhsEnd] <= rhs[rhsEnd])  
  66.                         {  
  67.                             rhsEnd = rhsEnd - 1;  
  68.                             if ((rhsEnd - rhsBegin) & 0x01)  
  69.                             {  
  70.                                 if (lhs[lhsEnd] <= rhs[(rhsBegin + rhsEnd) >> 1])  
  71.                                     result = rhs[(rhsBegin + rhsEnd) >> 1];  
  72.                                 else  
  73.                                     result = min(lhs[lhsEnd], rhs[(rhsBegin + rhsEnd) / 2 + 1]);  
  74.                             }  
  75.                             else  
  76.                             {  
  77.                                 if (lhs[lhsEnd] <= rhs[rhsMid])  
  78.                                     result = max(lhs[lhsEnd], rhs[rhsMid]);  
  79.                                 else  
  80.                                     result = rhs[rhsMid];  
  81.                             }  
  82.                         }  
  83.                         else  
  84.                         {  
  85.                             result = rhs[rhsMid];   
  86.                         }  
  87.                     }  
  88.                 }  
  89.                 else  
  90.                 {  
  91.                     //rsh must have one elem  
  92.                     if ((lhsEnd - lhsBegin) & 0x01)  
  93.                     {  
  94.                         if (rhs[rhsBegin] < lhs[lhsMid])  
  95.                             result = lhs[lhsMid];  
  96.                         else  
  97.                             result = min(rhs[rhsBegin], lhs[lhsMid + 1]);  
  98.                     }  
  99.                     else  
  100.                     {  
  101.                         if (rhs[rhsBegin] < lhs[lhsMid])  
  102.                             result = max(rhs[rhsBegin], lhs[lhsMid - 1]);  
  103.                         else  
  104.                             result = lhs[lhsMid];  
  105.                     }  
  106.                 }  
  107.                 break;  
  108.             }  
  109.             lhsBegin = lhsBegin + minValue;  
  110.             rhsEnd = rhsEnd - minValue;  
  111.         }   
  112.         else  
  113.         {  
  114.             lhsRight = lhsEnd - lhsMid;  
  115.             rhsLeft = rhsMid - rhsBegin;  
  116.             int minValue = min(lhsRight, rhsLeft);  
  117.             if (minValue == 0)  
  118.             {  
  119.                 if (lhsRight == 0 && rhsLeft == 0)  
  120.                 {  
  121.                     if ((rhsEnd - rhsBegin) & 0x01)  
  122.                     {  
  123.                         result = max(lhs[lhsBegin], rhs[rhsMid]);  
  124.                     }  
  125.                     else  
  126.                     {  
  127.                         result = min(lhs[lhsBegin], rhs[rhsMid]);  
  128.                     }  
  129.                 }  
  130.                 else if (rhsLeft == 0)  
  131.                 {  
  132.                     //rhs has one elem  
  133.                     if (rhsBegin == rhsEnd)  
  134.                     {  
  135.                         if ((lhsEnd - lhsBegin) & 0x01)  
  136.                         {  
  137.                             if (rhs[rhsBegin] < lhs[lhsMid])  
  138.                                 result = lhs[lhsMid];  
  139.                             else  
  140.                                 result = min(rhs[rhsBegin], lhs[lhsMid + 1]);  
  141.                         }  
  142.                         else  
  143.                         {  
  144.                             if (rhs[rhsBegin] < lhs[lhsMid])  
  145.                                 result = max(rhs[rhsBegin], lhs[lhsMid - 1]);  
  146.                             else  
  147.                                 result = lhs[lhsMid];         
  148.                         }  
  149.                     }  
  150.                     //rhs two elems  
  151.                     else  
  152.                     {  
  153.                         if (rhs[rhsEnd] <= lhs[lhsEnd])  
  154.                         {  
  155.                             lhsEnd = lhsEnd - 1;  
  156.                             if ((lhsEnd - lhsBegin) & 0x01)  
  157.                             {  
  158.                                 if (rhs[rhsEnd] <= lhs[(lhsBegin + lhsEnd) >> 1])  
  159.                                     result = lhs[(lhsBegin + lhsEnd) >> 1];  
  160.                                 else  
  161.                                     result = min(rhs[rhsEnd], lhs[(lhsBegin + lhsEnd) / 2 + 1]);  
  162.                             }  
  163.                             else  
  164.                             {  
  165.                                 if (rhs[rhsEnd] <= lhs[(lhsBegin + lhsEnd) >> 1])  
  166.                                     result = max(rhs[rhsEnd], lhs[(lhsBegin + lhsEnd) / 2 - 1]);  
  167.                                 else  
  168.                                     result = lhs[(lhsBegin + lhsEnd) >> 1];  
  169.                             }  
  170.                         }  
  171.                         else  
  172.                         {  
  173.                             result = lhs[lhsMid];   
  174.                         }  
  175.                     }  
  176.                 }  
  177.                 else  
  178.                 {  
  179.                     //lsh must have one elem  
  180.                     if ((rhsEnd - rhsBegin) & 0x01)  
  181.                     {  
  182.                         if (lhs[lhsBegin] < rhs[rhsMid])  
  183.                             result = rhs[rhsMid];  
  184.                         else  
  185.                             result = min(lhs[lhsBegin], rhs[rhsMid + 1]);  
  186.                     }  
  187.                     else  
  188.                     {  
  189.                         if (lhs[lhsBegin] < rhs[rhsMid])  
  190.                             result = max(lhs[lhsBegin], rhs[rhsMid - 1]);  
  191.                         else  
  192.                             result = rhs[rhsMid];  
  193.                     }  
  194.                 }  
  195.                 break;  
  196.             }  
  197.             lhsEnd = lhsEnd - minValue;  
  198.             rhsBegin = rhsBegin + minValue;  
  199.         }  
  200.     }  
  201.   
  202.     return result;  
  203. }  
  204.   
  205. void main()  
  206. {  
  207.     int lhs[] = {1};  
  208.     int rhs[] = {2, 3, 4};  
  209.     const int size1 = sizeof lhs / sizeof *lhs;  
  210.     const int size2 = sizeof rhs / sizeof *rhs;  
  211.     int result = findMidValue(lhs, size1, rhs, size2);  
  212.     cout << "mid value = " << result << endl;  
  213. }  

 

(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次

思想与本文实质相同!

 

本文转自: http://blog.csdn.net/alexingcool/article/details/7932441


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 分治法选择数组中的第k小元素的步骤如下: 1. 在数组中选择一个基准元素。 2. 将数组划分为小于基准元素、等于基准元素和大于基准元素的三个部分。 3. 如果第k小元素在小于基准元素的部分,则在这部分递归地查找第k小元素。 4. 如果第k小元素在等于基准元素的部分,则直接返回基准元素。 5. 如果第k小元素在大于基准元素的部分,则在这部分递归地查找第k-m小元素,其中m是等于基准元素的数量加上小于基准元素的数量。 ### 回答2: 分治法选择数组中的第k小元素的算法步骤如下: 1. 将原始数组划分为长度相等的子数组,直到每个子数组只包含一个元素。 2. 对每个子数组进行排序。 3. 从每个排序后的子数组中选择中位数(即元素个数为奇数时,选取中间的元素;元素个数为偶数时,选取中间两个元素的平均值)。 4. 将这些中位数构成一个新的数组作为中位数数组。 5. 递归地调用该算法,将中位数数组作为新的原始数组进行处理,直到中位数数组中只包含一个元素。 6. 找到中位数数组中的第k小元素,即为原始数组中的第k小元素。 这个算法基本上是利用了分治法的思想。通过对原始数组进行划分,将大的问题分解为小的子问题,并将子问题的结果进行合并,在每一次划分和合并的过程中,选取合适的元素进行排序和选择,最终得到原始数组中的第k小元素。算法的时间复杂度为O(nlogn),其中n为原始数组的长度,因为每一次划分和合并都需要进行数组排序和复制操作。 ### 回答3: 分治法选择数组中的第k小元素的算法步骤如下: 1. 将给定的数组分成若干个子数组。 2. 对每个子数组进行递归地找到其中的中位数。 3. 将这些中位数组成一个新的数组,称为中位数数组。 4. 对中位数数组进行递归地找到其中的中位数,即中位数数组中位数。 5. 将中位数数组中位数作为枢纽(pivot)。 6. 根据枢纽将原始数组划分为三个部分,小于枢纽的元素、等于枢纽的元素和大于枢纽的元素。 7. 根据k与三个部分的大小关系,决定继续在哪个部分继续查找第k小元素。 - 如果k小于等于小于枢纽的元素的数量,则在小于枢纽的部分继续查找第k小元素。 - 如果k大于小于枢纽的元素的数量且小于等于小于枢纽加等于枢纽的元素的数量,则返回枢纽作为第k小元素。 - 如果k大于小于枢纽加等于枢纽的元素的数量,则在大于枢纽的部分继续查找第k小元素。 8. 重复递归进行上述步骤,直到找到第k小元素。 这个算法的关键在于每次选择合适的枢纽,以尽量均匀地划分数组,从而使得每次递归的规模下降得快。平均时间复杂度为O(n),最坏情况下的时间复杂度为O(n^2),其中n为数组的长度。这个算法在时间复杂度上比简单的排序算法如冒泡排序和插入排序更优秀,适合用于需要找到数组中第k小元素的场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值