题目:从大量的数据中找出最小的K个数。
思路一:维护一个最大堆。时间复杂度O(nlogk),空间复杂度O(k)。
堆创建的核心函数是adjustDown(),将一个小值节点往下沉,直到它的子节点都比自己小。
void adjustDown(int *arr, int i, int n) {
int tmp = arr[i],j; //保存待调整的节点值tmp,左子节点j.
for (; 2 * i <= n;i=j) {
j=2*i;
if (j + 1 <= n&&arr[j + 1] > arr[j]) j++;//若右节点存在&&右节点值更大,取右节点.
if (arr[j] > tmp) arr[i] = arr[j]; //若最大的子节点比自己大,则将该子节点填充到自己的位置.
else break;
}
arr[i] = tmp;
}
//为了计算方便,数组的有效部分是从下标1开始的,arr[0]没有用到。
void makeHeap(int *arr, int n) {
for (int i = n / 2; i > 0; i--)
adjustDown(arr, i, n);
}
这里读取一行数据,用的是getline(cin,str)和sstream中的istringstream strstream(str),再用strstream依次>>arr[i].
int main() {
string str;
while (getline(cin, str)) {
//将输入写进数组(这里因为输入里没有预先的数组长度n,所以用到下面的处理方式)
istringstream strstream(str);
int n = count(str.begin(), str.end(), ' ');
int *arr = new int[n+2];
for (int i = 1; i < n+2; i++)
strstream >> arr[i];
//建堆并且不断更新
int k = arr[n+1];
makeHeap(arr, k);
for (int i = k + 1; i <= n; i++)
if (arr[i] < arr[1]) {
arr[1] = arr[i];
adjustDown(arr, 1, k);
}
//输出(按从小到大的顺序,所以需要排序)
int *out = new int[k];
for (int i = 0; i < k; i++){
out[k - i-1] = arr[1];
arr[1] = arr[k-i];
adjustDown(arr, 1, k - i-1);
}
for (int i = 0; i < k-1; i++)
cout << out[i]<<" ";
cout << out[k - 1] << endl;
}
}
思路二:快排的思想,但每次只排一半。时间复杂度O(n),空间复杂度O(1)。