我正在尝试编写一个算法,以O(n)的时间打印n大小数组中最小的k个数字,但我无法将时间复杂度降至n。我该如何做到这一点?
我试图编写一个O(n)时间复杂度的算法来打印n大小数组中最小的k个数字,但我无法在时间复杂度为n的情况下实现。请问有什么方法可以解决这个问题吗?我正在尝试编写一个算法,以O(n)的时间打印n大小数组中最小的k个数字,但我无法将时间复杂度降至n。我该如何做到这一点?
我试图编写一个O(n)时间复杂度的算法来打印n大小数组中最小的k个数字,但我无法在时间复杂度为n的情况下实现。请问有什么方法可以解决这个问题吗?我曾在一次面试中做过这个问题,其中最优雅/高效的方法之一是
O(n log k).
with space: O(k) (thanks, @Nzbuu)
基本上,你将使用一个大小限制为k的最大堆。对于数组中的每个项目,检查它是否小于最大值(仅O(1)时间)。如果是,则删除最大值并将其放入堆中(O(log k)时间)。如果它更大,则继续下一个项目。
当然,堆不会产生排序后的k个项目列表,但这可以在O(k log k)的时间内完成,非常容易。
同样地,你也可以找到最大的k个项目,这时候你需要使用一个最小堆。
您需要使用“选择算法”找到第k小的元素,该算法的时间复杂度为O(n),然后再次迭代数组并返回每个小于等于该元素的元素。
选择算法: http://en.wikipedia.org/wiki/Selection_algorithm
如果有重复项,您需要注意确保不返回超过k个元素(例如,如果有1、2、...、k、k、k、...)。
编辑:
完整算法,并要求返回一个列表:假设数组为A
。
1. find the k'th element in A using 'selection algorithm', let it be 'z'
2. initialize an empty list 'L'
3. initialize counter<-0
4. for each element in A:
4.1. if element < z:
4.1.1. counter<-counter + 1 ; L.add(element)
5. for each element in A:
5.1. if element == z AND count < k:
5.1.1. counter<-counter + 1 ; L.add(element)
6. return L
请注意,如果你的列表可能有重复项,则需要第三次迭代。 如果没有重复项,则不需要进行第三次迭代,只需将4.1中的条件更改为<=。
还要注意:L.add将一个元素插入到链表中,因此是O(1)。
假设您正在尝试显示K个最小的数字,您可以使用Hoare's Select算法来查找第k个最小的数字。这将把数组分成较小的数字、第k个数字和较大的数字。
能够在 O(n)
的时间内找到 n 个元素中的前 k 小数(我指的是真正的 O(n)
时间,而不是 O(n + 某个关于 k 的函数)
)。请参考维基百科“选择算法”的“无序部分排序”和“中位数选择作为枢轴策略”的子部分,以及“中位数”的文章,了解使得这种算法达到 O(n)
的重要组成部分。
这可以在预期的线性时间(O(n))内完成。首先使用枢轴分区方法找到数组的第kth
小元素(用于查找kth
顺序统计量),然后简单地迭代循环,检查哪些元素小于kth
最小元素。请注意,这仅对不同的元素有效。
以下是C代码:
/*find the k smallest elements of an array in O(n) time. Using the Kth order
statistic-random pivoting algorithm to find the kth smallest element and then looping
through the array to find the elements smaller than kth smallest element.Assuming
distinct elements*/
#include <stdio.h>
#include <math.h>
#include <time.h>
#define SIZE 10
#define swap(X,Y) {int temp=X; X=Y; Y=temp;}
int partition(int array[], int start, int end)
{
if(start==end)
return start;
if(start>end)
return -1;
int pos=end+1,j;
for(j=start+1;j<=end;j++)
{
if(array[j]<=array[start] && pos!=end+1)
{
swap(array[j],array[pos]);
pos++;
}
else if(pos==end+1 && array[j]>array[start])
pos=j;
}
pos--;
swap(array[start], array[pos]);
return pos;
}
int order_statistic(int array[], int start, int end, int k)
{
if(start>end || (end-start+1)<k)
return -1; //return -1
int pivot=rand()%(end-start+1)+start, position, p;
swap(array[pivot], array[start]);
position=partition(array, start, end);
p=position;
position=position-start+1; //size of left partition
if(k==position)
return array[p];
else if(k<position)
return order_statistic(array, start,p-1,k);
else
return order_statistic(array,p+1,end,k-position);
}
void main()
{
srand((unsigned int)time(NULL));
int i, array[SIZE],k;
printf("Printing the array...\n");
for(i=0;i<SIZE;i++)
array[i]=abs(rand()%100), printf("%d ",array[i]);
printf("\n\nk=");
scanf("%d",&k);
int k_small=order_statistic(array,0,SIZE-1,k);
printf("\n\n");
if(k_small==-1)
{
printf("Not possible\n");
return ;
}
printf("\nk smallest elements...\n");
for(i=0;i<SIZE;i++)
{
if(array[i]<=k_small)
printf("%d ",array[i]);
}
}
// Complexity is O(n log(n))
var source = [9, 2, 7, 11, 1, 3, 14, 22];
var kthMax = function(minInd, MaxInd, kth) {
// pivotInd stores the pivot position
// for current iteration
var temp, pivotInd = minInd;
if (minInd >= MaxInd) {
return source[pivotInd];
}
for (var i = minInd; i < MaxInd; i++) {
//If an element is greater than chosen pivot (i.e. last element)
//Swap it with pivotPointer element. then increase ponter
if (source[i] > source[MaxInd]) {
temp = source[i];
source[i] = source[pivotInd];
source[pivotInd] = temp;
pivotInd++;
}
}
// we have found position for pivot elem.
// swap it to that position place .
temp = source[pivotInd];
source[pivotInd] = source[MaxInd];
source[MaxInd] = temp;
// Only try to sort the part in which kth index lies.
if (kth > pivotInd) {
return kthMax(pivotInd + 1, MaxInd, kth);
} else if (kth < pivotInd) {
return kthMax(minInd, pivotInd - 1, kth);
} else {
return source[pivotInd];
}
}
// last argument is kth-1 , so if give 2 it will give you,
// 3rd max which is 11
console.log(kthMax(0, source.length - 1, 2));
另一种技术-使用QuickSelect算法,结果将是返回结果左侧的所有元素。平均时间复杂度为O(n),最坏情况下为O(n^2)。空间复杂度为O(1)。
private int[] FindKBiggestNumbersM(int[] testArray, int k)
{
int[] result = new int[k];
int indexMin = 0;
result[indexMin] = testArray[0];
int min = result[indexMin];
for (int i = 1; i < testArray.Length; i++)
{
if(i < k)
{
result[i] = testArray[i];
if (result[i] < min)
{
min = result[i];
indexMin = i;
}
}
else if (testArray[i] > min)
{
result[indexMin] = testArray[i];
min = result[indexMin];
for (int r = 0; r < k; r++)
{
if (result[r] < min)
{
min = result[r];
indexMin = r;
}
}
}
}
return result;
}