方法1.:维持一个大小为k最小堆,
- 后面来的数小或者等于堆顶元素,则跳过,;
- 后面来的数大于堆顶元素,堆顶元素弹出,新元素加入最小堆
最后留下的k个数就是,所有数中前k大的数,堆顶元素就是第k大的数
时间复杂度:由于维持大小为k的堆花费时间为log(k),所以时间复杂度为nlog(k).
代码如下:
//构建最小堆,当前数比父结点小就往上冒
void bulidMinHeap(int arr[], int index) {
while (arr[index] < arr[(index - 1) / 2]) {
swap(arr[index], arr[(index - 1) / 2]);
index = (index - 1) / 2;
}
}
//维持堆的结构,选取出当前数与其左右孩子中最小的数
//如果最小数为自身,退出
//如果为左右孩子,与其交换
void heapify(int arr[], int index, int heapSize) {
int leftChild = index * 2 + 1;
int smallestIndex;
while (leftChild < heapSize) {
smallestIndex = leftChild + 1 < heapSize && arr[leftChild] > arr[leftChild + 1]
? leftChild + 1 : leftChild;
smallestIndex = arr[index] > arr[smallestIndex] ? smallestIndex : index;
if (smallestIndex == index){
break;
}
swap(arr[index], arr[smallestIndex]);
index = smallestIndex;
leftChild = index * 2 + 1;
}
}
int calKthMin(int arr[], int k, int arrSize) {
//建立大小为k的最小堆
for (int i = 0; i < k; i++){
bulidMinHeap(arr, i);
}
//对后面的数进行处理
for (int i = k; i < arrSize; i++){
if (arr[i] <= arr[0]) continue;
else {
swap(arr[0], arr[i]);
heapify(arr, 0, k);
}
}
return arr[0];
}
使用c++STL代码如下;
注意:优先队列默认为less<int>参数,less越来越小之意,即最大堆
//priority_queue<int, vector<int>, greater<int>> minHeap;
//使用greater<int> 要加头文件#include <functional>
//priority_queue<int, vector<int>, less<int>> maxHeap;
int calKthMaxByQueue(int arr[], int k, int arrSize) {
priority_queue<int, vector<int>, greater<int>> minHeap;
for (int i = 0; i < k; i++) {
minHeap.push(arr[i]);
}
for (int i = k; i < arrSize; i++){
if (arr[i] <= minHeap.top()) continue;
else {
minHeap.pop();
minHeap.push(arr[i]);
}
}
return minHeap.top();
}
方法2:利用快排中partition函数的思想,一直partition到等于轴心数的区域包含arr[arrSize - k]
假设求第100大的数,那个排序之后该数就该在数组中的下标就该为1000 - 100 = 900
时间复杂度:每次选择的pivotNum数的好坏将影响时间,长期期望为O(n);
//partition过程,返回新子区间的上下界
vector<int> partition(int arr[], int leftIndex, int rightIndex) {
int lessBoundary = leftIndex - 1;
int biggerBoundary = rightIndex + 1;
int curIndex = leftIndex;
int randomIndex = rand() % (rightIndex - leftIndex + 1) + leftIndex;
swap(arr[randomIndex], arr[rightIndex]);
int pivotNum = arr[rightIndex];
while (curIndex < biggerBoundary) {
if (arr[curIndex] < pivotNum) {
swap(arr[curIndex++], arr[++lessBoundary]);
}
else if (arr[curIndex] > pivotNum) {
swap(arr[curIndex], arr[--biggerBoundary]);
}
else {
curIndex++;
}
}
vector<int> leftRightIndex(2);
leftRightIndex[0] = lessBoundary;
leftRightIndex[1] = biggerBoundary;
return leftRightIndex;
}
//kthIndex:第k大的数在数组中应该在的位置
//equalRange为本次partition得到的等于x的区间
//kthIndex在equalRange中停止,否则去子区间寻找
int process(int arr[], int kthIndex, int leftIndex, int rightIndex) {
vector<int> equalRange = partition(arr, leftIndex, rightIndex);
if (kthIndex < equalRange[1] && kthIndex > equalRange[0]) {
return arr[kthIndex];
}
else if (kthIndex <= equalRange[0]) {
return process(arr, kthIndex, leftIndex, equalRange[0]);
}
else{
return process(arr, kthIndex, equalRange[1], rightIndex);
}
}
int calKthMaxByPartition(int arr[], int k, int arrSize) {
return process(arr, arrSize - k, 0, arrSize - 1);
}