标签:表示 小问题 pre 时间复杂度 nbsp sea 操作 分区 要求
题目描述:存在两个长度为n得有序序列S1、S2,求出S1、S2合并后得有序序列的中位数,要求算法实间复杂度O(logn)
//输入 5 1 3 5 7 9 2 3 4 5 6 //输出 4
//输入 6 -100 -10 1 1 1 1 -50 0 2 3 4 5 //输出 1
本题很容易能够想到几种较简单的做法,但是复杂度都不太符合要求,首先最容易想到的时可以将两个数组合并在一
个数组中然后进行排序最快的排序复杂度也需要O(nlogn),第二种做法是利用归并排序的思想,将两个数组归并入一个
数组中,然后直接输出数组中间值,此时复杂度为O(n),因此需要利用二分法的思想。不断减低问题的规模,来提高
解决问题的效率。
通过二分思想,判断两个序列的中位数大小来不断减小问题规模。
1、判断两个有序序列的中位数,其中当两个中位数相等时,该数值就是所求的结果
2、若S1序列的中位数大于S2序列的中位数,则取S1的左半部分和S2的右半部分
3、若S1序列的中位数小于S2序列的中位数,则取S2的左半部分和S1的右半部分
原因:以第二种情况举例,若S1中位数大于S2中位数,那么S1的右半部分是既大于S1的左半部分和
大于S2的左半部分,这样就会导致S1的右半部分大于n+1个元素,则他肯定不可能是中位数的值,所以
将其去掉减小问题规模。同理,S2的左半部分也是小于n+1个元素,他也不可能是中位数的候选区域,
所以将其去掉,每次可以去掉约一半的元素。
举例:样例1中:
S1: 1 3 5 7 9 S2: 2 3 4 5 6
其中中位数5大于4,则他的右半部分元素(7、9)大于S1左半部分(1、3、5)和S2左半部分(2、3、4),因此(7、9)不可能为中位数所在区域
/*(这部分较详细...因为自己的问题,想不通了很久,实在是太愚蠢了(微笑))*/
通过上面不断二分的过程,无论是迭代还是递归最后都会剩下4个元素,而这四个元素取第二大的
就可以得到所要的结果了。若用aleft、aright表示S1区间的大小 bleft、bright表示S2区间的大小,
当满足:aleft+1==aright && aleft+1==aright即可。
注意点:在将元素分区时可能会出现两个序列分的区间长度不相等需要进行处理,
将amid = (aleft + aright )>>1;换成amid = (aleft + aright + 1) >>1
时间复杂度:每次都将数组大小缩小一半,最坏情况下,时间复杂度为O(logn).
空间复杂度:没有利用辅助空间 ,空间复杂度为O(1).
#include<iostream> using namespace std; int n,a[100009],b[100009]; int BinSearch(int aleft,int aright,int bleft,int bright) { if(aleft == aright && bleft == bright) return min(a[aleft],b[bleft]); if(aleft + 1 == aright && bleft + 1 == bright) //若只剩下4个元素,输出第二大元素即可 { if(a[aleft] <= b[bleft]) aleft++; else bleft++; return min(a[aleft],b[bleft]); } int amid = (aleft + aright ) >> 1; int bmid = (bleft + bright + 1) >> 1; if(a[amid] == b[bmid]) //若相等,则该值为中位数 return a[amid]; else if(a[amid] > b[bmid]) BinSearch(aleft,amid,bmid,bright); //减小问题规模 else BinSearch(amid,aright,bleft,bmid); } int main() { cin>>n; for(int i=0;i<n;i++) cin>>a[i]; for(int i=0;i<n;i++) cin>>b[i]; int result = BinSearch(0,n-1,0,n-1); cout<<result<<endl; return 0; }
心得体会:
本次上机实践没有即时全部做完,一些细节点卡住了自己的思路,导致进度不前,实践是很重要的,应该多操作打代码...变的更好。
标签:表示 小问题 pre 时间复杂度 nbsp sea 操作 分区 要求
原文地址:https://www.cnblogs.com/LjwCarrot/p/9821442.html