这道题的题目为:输入你个整数,找到其中最小的K个数。
解题思路如下:
1.数组对于输入的input数组,利用Partition排序对基于k的input数组对齐进行调整,调整后的input数组中位于第k个数的左边的数都小于k,右边的数都大于k。于是k左边的4个数就是最小的k个数,输出即可。
代码如下:
void Swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
//Partition排序
int Partition(int* input, int start, int end)
{
int begin = start;
int last = end;
while (begin<last)
{
while ((begin < last) && (input[begin] <= input[end]))
begin++;
while ((begin<last) && (input[last] >= input[end]))
last--;
Swap(&input[begin], &input[last]);
}
Swap(&input[begin], &input[end]);
return begin;
}
void MinKNumber1(int* input, int* output, int length, int k)
{
assert(input&&output);
if (length <= 0 || k <= 0 || k > length)
return;
int start = 0;
int end = length - 1;
//第一次快速排序
int index = Partition(input, start, end);
//根据二分思想找以k-1为基准的一次快排
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);
}
}
//打印最小的k个数
for (int i = 0; i < k; i++)
{
output[i] = input[i];
printf("%d ", output[i]);
}
}
但是基/Partition排序这种算法的实现会改变输入的input数组,并入适用于数据量较大的实现。所以针对本题算法做了如下改进:首先利用已输入input数组中前k个元素建立一个K个数的最大堆,从第k个数开始,每次拿一个数和堆顶元素比较,只有当这个数比堆顶元素小,则与堆顶元素交换,然后在向下调整一次建成新的大堆,直到然后遍历所有的数为止。
代码如下:
void AdjustDown(int array[], int size, int root)
{
int left = 2 * root + 1;
int right = 2 * root + 2;
if (left >= size)
{
return;
}
int max = left;
if (right<size&&array[right]>array[left])
{
max = right;
}
if (array[root] >= array[max])
{
return;
}
Swap(array + root, array + max);
AdjustDown(array, size, max);
}
void CreateHeap(int array[], int size)
{
for (int i = size / 2 - 1; i >= 0; i--)
{
AdjustDown(array, size, i);
}
}
//时间复杂度O(nlogn)
void MinKNumber2(int* input, int len, int k)
{
assert(input);
int i = 0;
//1.利用前K个数建大堆
CreateHeap(input, k);
//2.遍历后边的数,如果小于堆顶元素则入堆
for (int j = k; j < len; j++)
{
if (input[j]<input[0])
{
Swap(&input[0], &input[j]);
AdjustDown(input, k, 0);
}
}
for (int i = 0; i < k; i++)
{
Swap(&input[0], &input[k - i - 1]);
AdjustDown(input, k - i - 1, 0);
}
}