求两个排序数组的第K大元素--求全排列的第K大元素

来源:互联网 发布:非常网络易中天 编辑:程序博客网 时间:2024/06/11 15:47
2.1.5 求两个排序数组的第K大元素。
很多人首先想到的是merge,时间复杂度O(m+n).有没有更快的办法。O(log k)
我们可以考虑从k入手。如果我们每次都能够删除一个一定在第k大元
素之前的元素,那么我们需要进行k次。但是如果每次我们都删除一半呢?由于A和B都是有序
的,我们应该充分利用这里面的信息,类似于二分查找,也是充分利用了“有序”。
假设A和B的元素个数都大于k/2,我们将A的第k/2个元素(即A[k/2-1])和B的第k/2
个元素(即B[k/2-1])进行比较,有以下三种情况(为了简化这里先假设k为偶数,所得到的结
论对于k是奇数也是成立的):
A[k/2-1] == B[k/2-1]
A[k/2-1] > B[k/2-1]
A[k/2-1] < B[k/2-1]
如果A[k/2-1] < B[k/2-1],意味着A[0]到A[k/2-1]的肯定在A[B的top k元素的范围
内,换句话说,A[k/2-1不可能大于A[B的第k大元素。留给读者证明。
因此,我们可以放心的删除A数组的这k/2个元素。同理,当A[k/2-1] > B[k/2-1]时,可
以删除B数组的k/2个元素。
当A[k/2-1] == B[k/2-1]时,说明找到了第k大的元素,直接返回A[k/2-1]或B[k/2-1]
即可。
因此,我们可以写一个递归函数。那么函数什么时候应该终止呢?
当A或B是空时,直接返回B[k-1]或A[k-1];
当k=1是,返回min(A[0], B[0]);

当A[k/2-1] == B[k/2-1]时,返回A[k/2-1]或B[k/2-1]

double find_kth(int A[], int m, int B[], int n, int k) {//always assume that m is equal or smaller than nif (m > n) return find_kth(B, n, A, m, k);if (m == 0) return B[k - 1];if (k == 1) return min(A[0], B[0]);//divide k into two partsint pa = min(k / 2, m), pb = k - pa;if (A[pa - 1] < B[pb - 1])return find_kth(A + pa, m - pa, B, n, k - pa);else if (A[pa - 1] > B[pb - 1])return find_kth(A, m, B + pb, n - pb, k - pb);elsereturn A[pa - 1];}
2.1.12
求全排列的第K大元素。例如:123全排列。第五大元素是312.
笨方法:枚举+排序
巧妙方法:康托展开。
X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0! 其中,a为整数,并且0<=ai<i(1<=i<=n)。这就是康托展开

假设有n个不重复的元素,第k个排列是a1; a2; a3; :::; an,那么a1是哪一个位置呢?
我们把a1去掉,那么剩下的排列为a2; a3; :::; an, 共计n-1个元素,n-1个元素共有(n-1)!个排列,于是就可以知道a1 =k/(n-1)!。
同理,a2; a3; :::; an的值推导如下:
k2 = k%(n-1)!
a2 = k2/(n 2)!
::: = :::
kn-1 = kn-2%2!
an-1 = kn-1/1!
an = 0

例1 {1,2,3,4,5}的全排列,并且已经从小到大排序完毕
(1)找出第96个数
首先用96-1得到95  //下标从0开始,12345本身是第一个
用95去除4! 得到3余23
有3个数比它小的数是4
所以第一位是4
用23去除3! 得到3余5
有3个数比它小的数是4但4已经在之前出现过了所以第二位是5(4在之前出现过,所以实际比5小的数是3个)
用5去除2!得到2余1
有2个数比它小的数是3,第三位是3
用1去除1!得到1余0
有1个数比它小的数是2,第二位是2
最后一个数只能是1
所以这个数是45321
注意:如果是不连续的全排列,1,3,5,6的全排列。上题第一步的得到3余23,里面的3是指索引3. 另vector list=(1,3,5,6)。a1=list[3],就是6,然后用完之后要切记erase掉,因为一个元素的排序只出现一次,因为有时候会遇到a2=0,a3=0的情况,123全排列的第5大元素,a1=(5-1)/4!=2, k=4%2=0; a2=0/1!=0, a3=0/0!=0; 所以a1=list[2]=3,(删除 list[2])

a2=list[0]=1,(删除list[0]), 后a3=list[0]=2(如果不删除就是1,结果就是311);


template<typename Sequence>Sequence kth_permutation(const Sequence &seq, int k) {const int n = seq.size();Sequence S(seq);Sequence result;int base = factorial(n - 1);--k; // 康托编码从0 开始for (int i = n - 1; i > 0; k %= base, base /= i, --i) {auto pos = next(S.begin(), k / base);result.push_back(*pos);S.erase(pos);}result.push_back(S[0]); // 最后一个return result;}</span>

fg



0 0
原创粉丝点击