Method One:排序
排序,取前 K 個最大元素,排序的時間複雜度可以達到 O(NlogN)
//sort降序排序,取最大的K個
bool Cmp(int x, int y) {
return x > y;
}
int* topk(int *arr, int size, int K) {
int *ret = new int[K];
std::sort(arr, arr + size, Cmp);
memcpy(ret, arr, K*sizeof(int));
return ret;
}
Method Two:局部排序
局部排序,冒泡排序的思想,但是隻排 K 次,取出最大的 K 個,時間複雜度O(N*K)
int* topk(int* arr, int size, int K) {
int *ret = new int[K];
for (int i = 0; i < K; ++i) {
for (int j = 0; j < size-i-1; ++j) {
if (arr[j] > arr[j+1]) {
std::swap(arr[j], arr[j+1]);
}
}
}
memcpy(ret, arr+size-K, K*sizeof(int));
return ret;
}
Method Three:堆
只找到前 K 個最大的元素,不對他們進行排序,如果運氣很差,每次都入堆調整,時間複雜度O(N*log(K))
// 向下調整
void Adjust(int *arr, int size, int parent) {
int child = 2 * parent + 1;
while (child < size) {
if (child + 1 < size && arr[child] > arr[child+1]) {
child++;
}
if (arr[child] < arr[parent]) {
std::swap(arr[child], arr[parent]);
parent = child;
child = 2 * parent + 1;
}
else {
break;
}
}
}
int* topk(int *arr, int size, int K) {
int *ret = new int[K];
//建小堆
int end = (K - 1) >> 1;
while (end >= 0) {
Adjust(arr, K, end);
end--;
}
//將堆中元素換爲最大的K個
for (int i = K; i < size; ++i) {
if (arr[i] > arr[0])
std::swap(arr[0], arr[i]);
Adjust(arr, K, 0);
}
memcpy(ret, arr, K*sizeof(int));
return ret;
}
Method Four:隨機選擇 +partition
和快速排序的思想類似,只要我們在用 key 分左右兩部分的時候,分界點 pos==K 說明找到了最大的K個元素(都在pos左邊)
int partion(int* arr, int begin, int end) {
int left = begin;
int right = end - 1;
int key = arr[right];
while (left < right) {
while (arr[left] > key) {
left++;
}
if (left < right) {
arr[right] = arr[left];
right--;
}
while (arr[right] < key) {
right--;
}
if (left < right) {
arr[left] = arr[right];
left++;
}
}
arr[left] = key;
return left;
}
int* topk(int *arr, int begin, int end, int K) {
int pos = partion(arr, begin, end);
if (pos == K) {
int *ret = new int[K];
memcpy(ret, arr, K*sizeof(int));
return ret;
}
else if (pos > K) {
return topk(arr, begin, pos, K);
}
return topk(arr, pos+1, end, K);
}