Skip to content

Commit 8b5373b

Browse files
committed
hello-algo/codes/c/chapter_heap/top_k1.c
1 parent 61d9a29 commit 8b5373b

2 files changed

Lines changed: 213 additions & 0 deletions

File tree

codes/c/chapter_heap/my_heap1.c

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#include "../utils/common.h"
2+
3+
#define MAX_SIZE 5000
4+
5+
/* 大顶堆 */
6+
typedef struct {
7+
// size 代表的是实际元素的个数
8+
int size;
9+
// 使用预先分配内存的数组,避免扩容
10+
int data[MAX_SIZE];
11+
} MaxHeap;
12+
13+
// 函数声明
14+
void siftDown(MaxHeap *maxHeap, int i);
15+
void siftUp(MaxHeap *maxHeap, int i);
16+
int parent(MaxHeap *maxHeap, int i);
17+
18+
/* 构造函数,根据切片建堆 */
19+
MaxHeap *newMaxHeap(int nums[], int size) {
20+
// 所有元素入堆
21+
MaxHeap *maxHeap = (MaxHeap *)malloc(sizeof(MaxHeap));
22+
maxHeap->size = size;
23+
memcpy(maxHeap->data, nums, size * sizeof(int));
24+
for (int i = parent(maxHeap, size -1); i >= 0; i--) {
25+
// 堆化除页节点以外的其他所有节点
26+
siftDown(maxHeap, i);
27+
}
28+
return maxHeap;
29+
}
30+
31+
/* 析构函数 */
32+
void delMaxHeap(MaxHeap *maxHeap) {
33+
// 释放内存
34+
free(maxHeap);
35+
}
36+
37+
/* 获取左子节点的索引 */
38+
int left(MaxHeap *maxHeap, int i) {
39+
return 2 * i + 1;
40+
}
41+
42+
/* 获取右子节点的索引 */
43+
int right(MaxHeap *maxHeap, int i) {
44+
return 2 * i + 2;
45+
}
46+
47+
/* 获取父节点的索引 */
48+
int parent(MaxHeap *maxHeap, int i) {
49+
return (i - 1) / 2; // 向下取整
50+
}
51+
52+
/* 交换元素 */
53+
void swap(MaxHeap *maxHeap, int i, int j) {
54+
int temp = maxHeap->data[i];
55+
maxHeap->data[i] = maxHeap->data[j];
56+
maxHeap->data[j] = temp;
57+
}
58+
59+
/* 获取堆大小 */
60+
int size(MaxHeap *maxHeap) {
61+
return maxHeap->size;
62+
}
63+
64+
/* 判断堆是否为空 */
65+
int isEmpty(MaxHeap *maxHeap) {
66+
return maxHeap->size == 0;
67+
}
68+
69+
/* 访问堆定元素 */
70+
int peek(MaxHeap *maxHeap) {
71+
return maxHeap->data[0];
72+
}
73+
74+
/* 元素入堆 */
75+
void push(MaxHeap *maxHeap, int val) {
76+
// 默认情况下,不应该添加这么多节点
77+
if (maxHeap->size == MAX_SIZE) {
78+
printf("heap is full!");
79+
return;
80+
}
81+
// 添加节点
82+
maxHeap->data[maxHeap->size] = val;
83+
maxHeap->size++;
84+
85+
// 从底至顶堆化
86+
siftUp(maxHeap, maxHeap->size - 1);
87+
}
88+
89+
/* 元素出堆 */
90+
int pop(MaxHeap *maxHeap) {
91+
// 判空处理
92+
if (isEmpty(maxHeap)) {
93+
printf("heap is empty!");
94+
return INT_MAX;
95+
}
96+
// 交换根节点与最右叶节点(交换首元素与尾元素)
97+
swap(maxHeap, 0, size(maxHeap) - 1);
98+
// 删除节点
99+
int val = maxHeap->data[maxHeap->size - 1];
100+
maxHeap->size--;
101+
// 从顶至底堆化
102+
siftDown(maxHeap, 0);
103+
104+
// 返回堆顶元素
105+
return val;
106+
}
107+
108+
/* 从节点 i 开始,从顶至底堆化 */
109+
void siftDown(MaxHeap *maxHeap, int i) {
110+
while (true) {
111+
// 判断节点 i, l, r 中值最大的节点,记为 max
112+
int l = left(maxHeap, i);
113+
int r = right(maxHeap, i);
114+
int max = i;
115+
if (l < size(maxHeap) && maxHeap->data[l] > maxHeap->data[max]) {
116+
max = l;
117+
}
118+
if (r < size(maxHeap) && maxHeap->data[r] > maxHeap->data[max]) {
119+
max = r;
120+
}
121+
// 若节点 i 最大或索引 l, r 越界,则无需继续堆化,跳出
122+
if (max == i) {
123+
break;
124+
}
125+
// 交换两节点
126+
swap(maxHeap, i, max);
127+
// 循环向下堆化
128+
i = max;
129+
}
130+
}
131+
132+
/* 从节点 i 开始,从底至顶堆化 */
133+
void siftUp(MaxHeap *maxHeap, int i) {
134+
while (true) {
135+
// 获取节点 i 的父节点
136+
int p = parent(maxHeap, i);
137+
// 当“越过根节点”或“节点无需修复”时,结束堆化
138+
if (p < 0 || maxHeap->data[i] <= maxHeap->data[p]) {
139+
break;
140+
}
141+
// 交换两节点
142+
swap(maxHeap, i, p);
143+
// 循环向上堆化
144+
i = p;
145+
}
146+
}

codes/c/chapter_heap/top_k1.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#include "my_heap.c"
2+
3+
/* 元素入堆 */
4+
void pushMinHeap(MaxHeap *maxHeap, int val) {
5+
// 元素取反
6+
push(maxHeap, -val);
7+
}
8+
9+
/* 元素出堆 */
10+
int popMinHeap(MaxHeap *maxHeap) {
11+
// 元素取反
12+
return -pop(maxHeap);
13+
}
14+
15+
/* 访问堆顶元素 */
16+
int peekMinHeap(MaxHeap *maxHeap) {
17+
// 元素取反
18+
return -peek(maxHeap);
19+
}
20+
21+
/* 取出堆中元素 */
22+
int *getMinHeap(MaxHeap *maxHeap) {
23+
// 将堆中所有元素取反并存入 res 数组
24+
int *res = (int *)malloc(maxHeap->size * sizeof(int));
25+
for (int i = 0; i < maxHeap->size; i ++) {
26+
res[i] = -maxHeap->data[i];
27+
}
28+
return res;
29+
}
30+
31+
// 基于堆查找数组中最大的 k 个元素的函数
32+
int *topKHeap(int *nums, int sizeNums, int k) {
33+
// 初始化小顶堆
34+
// 请注意:我们将堆中的所有元素取反,从而用大顶堆来模拟小顶堆
35+
int *empty = (int *)malloc(0);
36+
MaxHeap *maxHeap = newMaxHeap(empty, 0);
37+
// 将数组的前 k 个元素入堆
38+
for (int i = 0; i < k; i++) {
39+
pushMinHeap(maxHeap, nums[i]);
40+
}
41+
// 从第 k+1 个元素开始,保持堆的长度为 k
42+
for (int i = k; i < sizeNums; i++) {
43+
// 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆
44+
if (nums[i] > peekMinHeap(maxHeap)) {
45+
popMinHeap(maxHeap);
46+
pushMinHeap(maxHeap, nums[i]);
47+
}
48+
}
49+
int *res = getMinHeap(maxHeap);
50+
// 释放内存
51+
delMaxHeap(maxHeap);
52+
return res;
53+
}
54+
55+
/* Driver Code */
56+
int main() {
57+
int nums[] = {1, 7, 6, 3, 2};
58+
int k = 3;
59+
int sizeNums = sizeof(nums) / sizeof(nums[0]);
60+
61+
int *res = topKHeap(nums, sizeNums, k);
62+
printf("最大的 %d 个元素为: ", k);
63+
printArray(res, k);
64+
65+
free(res);
66+
return 0;
67+
}

0 commit comments

Comments
 (0)