题目
![](https://img-blog.csdnimg.cn/img_convert/756db3e01580a833c9938ea79e5c78c7.png)
![](https://img-blog.csdnimg.cn/img_convert/79d469ec284b9f1d5c537902c4beda76.png)
题解
本题目的就是求数组中前k个大的数,因此我将它拿来练习一下堆排序。
堆排序可以做出来,但结果会不符合力扣答案按序号的要求。
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
//建立小根堆解决问题
//堆结构
typedef struct Heap{
int* data;
int size;
}Heap;
//堆初始化
void initialHeap(Heap* h){
for(int i = (h->size-1)/2;i >= 1;i--){
//特殊情况:序号最大的非叶节点可能会没有右孩子
if (i == (h->size - 1) / 2 && i * 2 + 1 == h->size) {
if (h->data[i * 2] < h->data[i]) {
int temp = h->data[i];
h->data[i] = h->data[2 * i];
h->data[2 * i] = temp;
}
continue;
}
//其他调整调整
if(h->data[i*2]<h->data[i] && h->data[i*2]<=h->data[2*i+1]){
int temp = h->data[i];
h->data[i] = h->data[2*i];
h->data[2*i] = temp;
}else if(h->data[i*2+1]<h->data[i] && h->data[i*2+1]<=h->data[2*i]){
int temp = h->data[i];
h->data[i] = h->data[2*i+1];
h->data[2*i+1] = temp;
}
}
}
//更新堆
void renewHeap(Heap* h, int x){
if(x <= h->data[1]) return;
h->data[1] = x;
//从上往下调整,把最小的放在根上
int index = 1; //目前正在调整的索引
while(index <= (h->size-1)/2){
//特殊情况:序号最大的非叶节点可能会没有右孩子
if (index == (h->size - 1) / 2 && index * 2 + 1 == h->size) {
if (h->data[index * 2] < h->data[index]) {
int temp = h->data[index];
h->data[index] = h->data[2 * index];
h->data[2 * index] = temp;
}
break;
}
//其他非叶结点的调整
if(h->data[index]<=h->data[2*index] && h->data[index]<=h->data[2*index+1]){ //该索引位置已经比左右两孩子的值都小了,无需调整
break;
}
if(h->data[index*2]<h->data[index] && h->data[index*2]<=h->data[2*index+1]){
int temp = h->data[index];
h->data[index] = h->data[2*index];
h->data[2*index] = temp;
index = index*2;
}else if(h->data[index*2+1]<h->data[index] && h->data[index*2+1]<=h->data[2*index]){
int temp = h->data[index];
h->data[index] = h->data[2*index+1];
h->data[2*index+1] = temp;
index = index*2+1;
}
}
}
int* maxSubsequence(int* nums, int numsSize, int k, int* returnSize){
//声明一个堆,其中堆的大小是k+1,因为索引为0的位置不用,实际存放的数据还是k个。
Heap* h = (Heap*)malloc(sizeof(Heap));
h->size = k+1;
h->data = (int*)malloc(sizeof(int)*h->size);
//往堆内填充初试数据
for(int i = 0;i < k;i++){
h->data[i+1] = nums[i];
}
//初始化堆
initialHeap(h);
//更新堆
for(int i = k;i < numsSize;i++){
renewHeap(h,nums[i]);
}
//要符合返回结果的格式,将堆中的元素移至从索引0开始
*returnSize = k;
for(int i = 0;i < k;i++){
h->data[i] = h->data[i+1];
}
return h->data;
}
![](https://img-blog.csdnimg.cn/img_convert/0e9141f40cc9f6e8bf5e6762f7b57599.png)
要点
堆排序的使用:先填充数组;然后初始化数组(从序号最大的非叶结点开始从下至上);最后依次更新数组,更新的过程是替换根结点之后从上至下进行调整,且只调整被涉及到的结点。
堆排序和完全二叉树一样,如果存储在数组中,是从索引为1的位置开始存储。只有这样才符合索引为i的结点其左孩子索引为2i,其右孩子索引为2i+1的公式。
堆排序要考虑最后一个非叶结点可能会出现没有右孩子的情况。
若要求前k个最大值的话,堆排序建立最小堆。
若要求前k个最小值的话,堆排序建立最大堆。