取一个集合的前n个最大或最小值

 

《编程珠玑》第十四章的一个问题是:

在具有10亿个数值的文件中找出最大的100万个数组。

 

使用堆解决这个问题的思路

1. 取10亿个数值中的最初的100万个,放到一个堆中,初始化这个堆为小顶堆。

2. 依次取剩下的每个数值,用取出的这个值和小顶堆的最小值比较:

   如果大于最小值,用这个值替换小顶堆的最小值,调整堆使得它还是小顶堆。

   如果小于或者等于最小值,则继续取下一个值。

最后这个堆包含的就是100万个最大值。

 

这个问题简单推广就是:寻找一个无序集合中前n个最大或者最小的值。也等价于:寻找一个无序集合中理解。这个思路好理解。尝试严格证明这个思路。

M是一个集合,要取其中的前n个最大值。

假设A是初始的n个元素组成的堆,则剩下的元素组成集合X=M-A。假想存在一个集合B,它存放X中的元素和A中最小值比较后的较小的值。对一个元素x(i)(属于X, i表示第i个元素),它和A的最小值比较,如果x(i)大,则把x(i)放入A,把A的最小值放入假想的集合B中(实际没有放入,假想),记此时的A为A(i),B为B(i)。假设X中元素有N个,则A,B对应的变化是:

A(0),A(1),…,A(N-1),A(N)

B(0),B(1),…,B(N-1),B(N)

A(N)的元素就是前n个最大的值。

我们需要证明的就是任意一个属于B(N)的元素,一定不大于A(N)中的任意一个元素,或者说一定不大于A(N)中的最小值。

证明:

假设元素b属于B(N),则b来自于A中,或者来至于X中。

如果b来至于A中,则b一定是某次A的最小值,然后被X中的一个比它大的值替换。则一定存在A(j-1)(0<=j<=N-1),满足b=Min(A(j-1)),同时b<Min(A(j))。我们知道每次A变化时,一定是一个较大的值替换了最小的值,所以Min(A(0))<=Min(A(1))<=…<=Min(A(N))。所以b<Min(A(j))<=Min(A(N))。

如果b来至于X中,则一定是从X中取出b和对应A(j)比较时,b<=Min(A(j))。所以b<=Min(A(j))<=Min(A(N))。

所以B(N)中的任意一个元素不大于A(N)的最小元素。

你可能感兴趣的:(取一个集合的前n个最大或最小值)