从“数组中出现次数超过一半的数字”得到启发,同样可以基于Partition函数来解决。
一、O(n)算法
void GetLeastNumbers(int *input, int n, int *output, int k)
{
if (input == NULL || output == NULL || k > n || n <= 0 || k <= 0)
return;
int start = 0;
int end = n - 1;
int index = Partition(input, start, end);
while (index != k -1)
{
if (index > k - 1)
{
end = index - 1;
index = Partition(input, start, end);
}
else
{
start = index + 1;
index = Partition(input, start, end);
}
}
for (int i = 0; i < k; ++i)
output[i] = input[i];
}
二、O(nlogK)算法
可以先创建一个大小为K的数据容器来存储最小的K个数字,姐下来我们每次从输入的n个整数中读入一个数。如果容器中已有的数字少于k个,则直接把这次读入的整数放入容器中;如果容器中已有k个数,也就是容器已满,此时我们不能再插入新的数字而只能替换已有的数字。找出这已有的k个数的最大值,然后拿这次待插入的整数和最大值进行比较。如果待插入的值比当前已有的最大值还要大,那么直接丢弃。
很容易想到最大堆,于是我们每次可以在O(1)得到已有的k个数字的最大值,但需要O(logk)时间完成删除及插入操作
// “下沉”,位置pos处开始
void sink(int pos, int A[], int N)
{
init j;
while (2 * pos <= N)
{
j = 2 * pos;
if (j < N && A[j] <= A[j+1])
j++;
if (A[pos] >= A[j])
break;
swap(A, pos, j);
pos = j; // “下沉”到其子节点
}
}
// array:输入数组
// A[0]未用
for i = 0 to K
A[i+1] = array[i]
end for
// 建立大根堆:将数组中的前K个元素构建成一个最大堆
void BuildHeap(int A[], int K) // K个元素,完全二叉树存储
{
for (int i = K / 2; i >= 1; --i)
{
sink(i, A, K);
}
}
// 数组后面的元素,往最大堆插入(待插元素比堆顶元素小) 或者 丢弃(待插元素大于堆顶元素)
for i = K + 1 to N
if array[i] < A[1]
A[1] = array[i]
sink(1, A, K)
end if
end for
二、利用STL set和multiset(允许重复)底层基于红黑树实现的机制
typedef multiset<int, greater<int> > intset; // 按从大到小排序
typedef multiset<int, greater<int> >::iterator setIterator;
void GetLeasetNumbers(const vector<int> &data, intset &leastNumbers, int k)
{
leastNumbers.clear();
if (k < 1 || data.size() < k)
return;
vector<int>::const_iterator iter = data.begin();
for (; iter != data.end(); ++iter)
{
// 前k个元素直接插入到容器中
if ((leastNumbers.size()) < k)
leastNumbers.insert(*iter);
else
{
setIterator iterGreatest = leastNumbers.begin();
if (*iter < *(leastNumbers.begin()))
{
leastNumbers.erase(iterGreatest);
leastNumbers.insert(*iter);
}
}
}
}