抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

回顾 - 二叉树

完全二叉树

  1. 编号为 i 的字节点

左孩子编号 :2 * i

右孩子编号 :2 * i + 1

  1. 可以使用连续空间存储 (数组)

image-20220122143259741

image-20220122144757632

  1. 大顶堆

    在任意一个三元组中 根 大于左孩子和右孩子 12 - 11 - 10 根节点 (极大) 全局最大

  2. 小顶堆

    在任意一个三元组中 根 小于左孩子和右孩子 3 - 7 - 4 根节点 (极小) 全局最小

堆 - 尾部插入调整 (自下向上)

依次和和根比较

13 > 7 交换

13 > 11 交换

13 > 12 交换

image-20220122150010932

image-20220122150118220

image-20220122150212660

堆 - 头部弹出调整 (自上向下)

从堆的最后元素直接放到堆顶

之后在在根节点的三元组中比较是是否满足条件 如果不满足最大值和根交换一直操作

12 > 7 交换

11 > 7 交换

image-20220122151807208

image-20220122151936505

时间复杂度分析

插入调整时间复杂度 - 层序遍历 : $O(log^N)$

删除调整时间复杂度 $O(log^N)$

建堆时间复杂度 : $O(Nlog^N)$

堆排序 - (大顶堆情况)

口诀:

  1. 将堆顶元素与堆尾元素交换
  2. 将此操作看最做是堆顶元素弹出操作
  3. 按照头部弹出以后的策略调整推

如果想要一个从小到大的排序 那么就建立一个大顶堆

如果想要一个从大到小的排序 那么就建立一个小顶堆

堆 - 优先队列

image-20220122153935337

堆代码实现 - (大顶堆举例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*************************************************************************
> File Name: 堆-优先队列.c
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年01月22日 星期六 15时45分42秒
************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define swap(a, b) {\
__typeof(a) __temp = a;\
a = b; b = __temp;\
}

typedef struct priority_queue {
int *data;
int cnt, size;
} priority_queue;

priority_queue *init(int n) {
priority_queue *q = (priority_queue *)malloc(sizeof(priority_queue));
q->data = (int *)malloc(sizeof(int) * n + 1);
q->size =n;
q->cnt = 0;
return q;
}

int empty(priority_queue *q) {
return q->cnt == 0;
}

int top(priority_queue *q) {
return q->data[1];
}

int push(priority_queue *q, int val) {
if(q == NULL) return 0;
if(q->cnt == q->size) return 0;
q->data[++(q->cnt)] = val;
int ind = q->cnt;
// 大顶堆
while(ind >> 1 && q->data[ind] > q->data[ind >> 1]) {
swap(q->data[ind], q->data[ind >> 1]);
ind >>= 1;
}
return 1;
}

int pop(priority_queue *q) {
if(q == NULL) return 0;
if(empty(q)) return 0;
q->data[1] = q->data[q->cnt--];
int ind = 1;
// ind << 1 <= q->cnt 证明左孩子存在
while((ind << 1) <= q->cnt) {
int temp = ind, l = ind << 1, r = ind << 1 | 1;
if(q->data[l] > q->data[temp]) temp = l;
// r <= q->cnt 证明右孩子存在
if(r <= q->cnt && q->data[r] > q->data[temp]) temp = r;
if(ind == temp) break;
swap(q->data[ind], q->data[temp]);
ind = temp;
}
return 1;
}

void clear(priority_queue * q) {
if(q == NULL) return ;
free(q->data);
free(q);
return ;
}

int main() {
srand(time(0));
#define MAX_OP 20
priority_queue *q = init(MAX_OP);
for(int i = 0; i < MAX_OP; i++) {
int val = rand() % 100;
push(q, val);
printf("insert %d to the priority_queue!\n", val);
}

for(int i = 0; i < MAX_OP; i++) {
printf("%d ", top(q));
pop(q);
}
printf("\n");
return 0;
}

线性建堆法 - (自上向下)

向上调整次数分析

在第 1 层时候(0表示向上调整0次) $0 * 2^0$ ($2^0$代表第一层有多少个节点)

第三次层每个节点最多向上调整2次

$0 * 2^1 + 1 * 2^1 + 2 * 2^2 + 3 + 2^3 + … + (n - 1) * 2^{n-1}$

0次 1个节点 1次 2个节点 … n - 1次 $2^{n-1}$节点

$0 * 2^{n - 1} + 1 * 2^{n - 2}$

堆排序 - 代码实现(线性建堆法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/*************************************************************************
> File Name: 堆-排序.c
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年01月22日 星期六 19时34分46秒
************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define swap(a, b) {\
__typeof(a) __temp = a;\
a = b; b = __temp;\
}

void downUpdate(int *arr, int n, int ind) {
while((ind << 1) <= n) {
int temp = ind, l = ind << 1, r = (ind << 1) + 1;
// if(arr[l] > arr[temp]) temp = l;
if(arr[l] < arr[temp]) temp = l;
// if(r <= n && arr[r] > arr[temp]) temp = r;
if(r <= n && arr[r] < arr[temp]) temp = r;
if(ind == temp) break;
swap(arr[ind], arr[temp]);
ind = temp;
}
return ;
}

void heap_Sort(int *arr, int n) {
arr -= 1;
for(int i = n >> 1; i >= 1; i--) {
downUpdate(arr, n, i);
}
for(int i = n; i > 1; i--) {
swap(arr[i], arr[1]);
downUpdate(arr, i - 1, 1);
}
return ;
}

void output(int *arr, int n) {
for(int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}

int main() {
srand(time(0));
#define MAX_N 20
int *arr = (int *)malloc(sizeof(int) * MAX_N);
for(int i = 0; i < MAX_N; i++) {
arr[i] = rand() % 100;
}
output(arr, MAX_N);
printf("\n");
heap_Sort(arr, MAX_N);
output(arr, MAX_N);
free(arr);
#undef MAX_N
return 0;
}

时间复杂度分析

$O(log^N)$

评论