您的位置:首页 > 其它

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

2014-06-29 22:19 357 查看
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 n
if (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 parts
int 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);
else
return 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐