完全二叉树:
1
、
编号为
i
的子节点:
左孩子编号:
2
*
i
右孩子编号:
2
*
i
+
1
2
、
可以用连续空间存储(数组)
完全二叉树在计算机中用连续的存储区域存储:
计算机中实现:线性结构
思维逻辑结构:树形结构
堆结构
堆结构是基于完全二叉树所提出来的一种结构
堆结构的定义:在堆的这个二叉树的结构中,任意一个三元组(根节点和左右子节点)都满足相应堆性质的定义
大顶堆/大根堆:根节点储存的是最大值
小顶堆/小根堆:根节点存储的是最小值
堆的插入
1.尾部插入,向上调整
堆的弹出
头部弹出,向下调整
普通队列
|
优先队列(大根堆和小根堆)
|
尾部入队
|
尾部可以插入
|
头部出队
|
头部可以弹出
|
先进先出
|
每次出队权值(最大
/
最小的元素)
|
数组实现
|
数组实现,
逻辑上看成一个堆
|
PriorityQueue 代码演示:
#include
<cstdio>
#include
<cstdlib>
#include
<cstring>
#include
<vector>
#include
<iostream>
using
namespace
std;
#define
cmp
>
#define
ROOT
1
#define
FATHER
(i) ((i) / 2)
#define
LEFT
(i) ((i) * 2)
#define
RIGHT
(i) ((i) * 2 + 1)
#define
swap
(a, b){\
printf(
"swap(%d, %d)\n"
, a, b);\
int
__c= (a);\
(a) = (b);\
(b) = __c;\
}
typedef
struct
PriorityQueue
{
//data指向_data的后一位,_data的index从1开始
//这样father: i, left: 2 * i, right: 2 * i + 1;
int
* _data, * data;
int
size, n;
}
PriorityQueue
;
PriorityQueue
* initPQ(
int
size
) {
PriorityQueue
* p = (
PriorityQueue
*)malloc(
sizeof
(
PriorityQueue
));
p->_data = (
int
*)malloc(
sizeof
(
int
) *
size
);
p->data = p->_data -
ROOT
;
p->size =
size
;
p->n = 0;
return
p;
}
int
empty(
PriorityQueue
*
p
) {
return
p
->n == 0;
}
int
full(
PriorityQueue
*
p
) {
return
p
->size ==
p
->n;
}
int
top(
PriorityQueue
*
p
) {
return
p
->data[
ROOT
];
}
void
up_update(
int
*
data
,
int
i
) {
printf(
"\nUP uodate : %d\n"
,
data
[
i
]);
while
(
i
>
ROOT
&&
data
[
i
] >
data
[
FATHER
(
i
)]) {
swap
(
data
[
i
],
data
[
FATHER
(
i
)]);
i
=
FATHER
(
i
);
}
printf(
"\n"
);
return
;
}
int
push(
PriorityQueue
*
p
,
int
x
) {
if
(full(
p
))
return
0;
p
->n += 1;
p
->data[
p
->n] =
x
;
up_update(
p
->data,
p
->n);
return
1;
}
void
down_update(
int
*
data
,
int
i
,
int
n
) {
printf(
"\ndown update : %d\n"
,
data
[
i
]);
while
(
LEFT
(
i
) <=
n
) {
//father 和 right/left 比较
int
ind =
i
, l =
LEFT
(
i
), r =
RIGHT
(
i
);
if
(
data
[l]
cmp
data
[ind]) ind = l;
if
(r <=
n
&&
data
[r]
cmp
data
[ind]) ind = r;
if
(ind ==
i
)
break
;
swap
(
data
[
i
],
data
[ind]);
i
= ind;
}
printf(
"\n"
);
return
;
}
int
pop(
PriorityQueue
*
p
) {
if
(empty(
p
))
return
0;
p
->data[
ROOT
] =
p
->data[
p
->n];
p
->n -= 1;
down_update(
p
->data,
ROOT
,
p
->n);
return
1;
}
void
clearPQ(
PriorityQueue
*
p
) {
if
(
p
==
NULL
)
return
;
free(
p
->_data);
free(
p
);
return
;
}
void
output(
PriorityQueue
*
p
) {
printf(
"PQ(%d) : "
,
p
->n);
for
(
int
i = 1; i <=
p
->n; i++) {
printf(
"%d "
,
p
->data[i]);
}
printf(
"\n"
);
return
;
}
int
main() {
int
op, x;
#define
MAX_OP
100
PriorityQueue
* p = initPQ(
MAX_OP
);
while
(~scanf_s(
"%d"
, &op)) {
if
(op == 1) {
scanf_s(
"%d"
, &x);
printf(
"insert %d to priority_queue : \n"
, x);
push(p, x);
//push
output(p);
}
else
{
printf(
"pop : %d\n"
, top(p));
pop(p);
//pop
output(p);
}
}
clearPQ(p);
return
0;
}
堆排序
普通建堆法:向上调整(O(n*logn))
从数组首位开始扫描,并把当前位置当成堆的末尾新插入元素的位置,开始向上调整;
线性建堆法:向下调整(O(n))
将数组看成堆,并从堆的倒数第二行开始向下调整
#define
cmp
>
#define
ROOT
1
#define
FATHER
(i) ((i) / 2)
#define
LEFT
(i) ((i) * 2)
#define
RIGHT
(i) ((i) * 2 + 1)
//#define swap(a, b) {\
// int c = (a); \
// (a) = (b); \
// (b) = c;\
//}
#define
TEST
(func, arr, n) { \
printf(
"TEST : %s "
, #func); \
int
*temp = (
int
*)malloc(
sizeof
(
int
) * n); \
memcpy(temp, arr,
sizeof
(
int
) * n); \
long
long
b = clock(); \
func(temp, n); \
long
long
e = clock(); \
if
(check(temp, n)){ \
printf(
"OK\t"
); \
}
else
{ \
printf(
"FAIL\t"
); \
} \
printf(
"%lld ms\n"
, 1000 * (e - b) /
CLOCKS_PER_SEC
);\
free(temp); \
}
//生成随机数组
int
* getRandData(
int
n
) {
int
* arr = (
int
*)malloc(
sizeof
(
int
) *
n
);
for
(
int
i = 0; i <
n
; i++) {
arr[i] = rand() % 100000;
}
return
arr;
}
bool
check(
int
*
arr
,
int
n
) {
for
(
int
i = 1; i <
n
; i++) {
if
(
arr
[i] >
arr
[i - 1])
return
false
;
}
return
true
;
}
inline
void
up_update(
int
*
data
,
int
i
) {
while
(
i
> 1 &&
data
[
i
]
cmp
data
[
FATHER
(
i
)]) {
swap(
data
[
i
],
data
[
FATHER
(
i
)]);
i
=
FATHER
(
i
);
}
return
;
}
inline
void
down_update(
int
*
data
,
int
i
,
int
n
) {
while
(
LEFT
(
i
) <=
n
) {
int
ind =
i
, l =
LEFT
(
i
), r =
RIGHT
(
i
);
if
(
data
[l]
cmp
data
[ind]) ind = l;
if
(r <=
n
&&
data
[r]
cmp
data
[ind]) ind = r;
if
(ind ==
i
)
break
;
swap(
data
[ind],
data
[
i
]);
i
= ind;
}
return
;
}
inline
void
normal_heap_build(
int
*
data
,
int
n
) {
for
(
int
i = 2; i <=
n
; i++) {
up_update(
data
, i);
}
return
;
}
inline
void
linear_heap_build(
int
*
data
,
int
n
) {
for
(
int
i =
n
/ 2; i >= 1; i--) {
down_update(
data
, i,
n
);
}
return
;
}
void
heap_sort_final(
int
*
data
,
int
n
) {
for
(
int
i =
n
; i >= 2; i--) {
swap(
data
[i],
data
[1]);
down_update(
data
,
ROOT
, i - 1);
}
}
void
normal_heap(
int
*
arr
,
int
n
) {
int
* data =
arr
- 1;
normal_heap_build(data,
n
);
heap_sort_final(data,
n
);
return
;
}
void
linear_heap(
int
*
arr
,
int
n
) {
int
* data =
arr
- 1;
linear_heap_build(data,
n
);
heap_sort_final(data,
n
);
return
;
}
int
main() {
srand(time(0));
#define
MAX_N
10000000
int
* arr = getRandData(
MAX_N
);
TEST
(normal_heap, arr,
MAX_N
);
TEST
(linear_heap, arr,
MAX_N
);
return
0;
}
堆排序优化哈夫曼编码
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#define
swap
(a, b) { \
__typeof(a) __c = a; \
a = b, b = __c; \
}
typedef
struct
Node
{
char
ch;
int
freq;
struct
Node
*lchild, *rchild;
}
Node
;
typedef
struct
Heap
{
// __data指向开辟空间的地址首位,data指向堆的首位,堆存储的是地址可以节省空间
Node
**__data, **data;
int
n, size;
}
Heap
;
Heap
*getNewHeap(
int
size
) {
Heap
*h = (
Heap
*)malloc(
sizeof
(
Heap
));
h->__data = (
Node
**)malloc(
sizeof
(
Node
*) *
size
);
h->data = h->__data - 1;
h->n = 0;
h->size =
size
;
return
h;
}
int
fullHeap(
Heap
*
h
) {
return
h
->n ==
h
->size;
}
int
emptyHeap(
Heap
*
h
) {
return
h
->n == 0;
}
Node
*top(
Heap
*
h
) {
if
(emptyHeap(
h
))
return
NULL
;
return
h
->data[1];
}
int
cmpHeap(
Heap
*
h
,
int
i
,
int
j
) {
return
h
->data[
i
]->freq <
h
->data[
j
]->freq;
}
void
up_maintain(
Heap
*
h
,
int
i
) {
while
(
i
> 1 && cmpHeap(
h
,
i
,
i
/ 2)) {
swap
(
h
->data[
i
],
h
->data[
i
/ 2]);
i
=
i
/ 2;
}
return
;
}
void
down_maintain(
Heap
*
h
,
int
i
) {
while
(
i
* 2 <=
h
->n) {
int
ind =
i
, l =
i
* 2, r =
i
* 2 + 1;
if
(cmpHeap(
h
, l, ind)) ind = l;
if
(r <=
h
->n && cmpHeap(
h
, r, ind)) ind = r;
if
(ind ==
i
)
return
;
swap
(
h
->data[
i
],
h
->data[ind]);
i
= ind;
}
return
;
}
int
pushHeap(
Heap
*
h
,
Node
*
n
) {
if
(fullHeap(
h
))
return
0;
h
->n += 1;
h
->data[
h
->n] =
n
;
up_maintain(
h
,
h
->n);
return
1;
}
int
popHeap(
Heap
*
h
) {
if
(emptyHeap(
h
))
return
0;
h
->data[1] =
h
->data[
h
->n];
h
->n -= 1;
down_maintain(
h
, 1);
return
1;
}
void
clearHeap(
Heap
*
h
) {
if
(
h
==
NULL
)
return
;
free(
h
->__data);
free(
h
);
return
;
}
Node
*getNewNode(
int
freq
,
char
ch
) {
Node
*p = (
Node
*)malloc(
sizeof
(
Node
));
p->ch =
ch
;
p->freq =
freq
;
p->lchild = p->rchild =
NULL
;
return
p;
}
void
swap_node(
Node
**
node_arr
,
int
i
,
int
j
) {
Node
*temp =
node_arr
[
i
];
node_arr
[
i
] =
node_arr
[
j
];
node_arr
[
j
] = temp;
return
;
}
int
find_min_node(
Node
**
node_arr
,
int
n
) {
int
ind = 0;
for
(
int
j = 1; j <=
n
; j++) {
if
(
node_arr
[ind]->freq >
node_arr
[j]->freq) ind = j;
}
return
ind;
}
Node
*buildHaffmanTree(
Node
**
node_arr
,
int
n
) {
Heap
*h = getNewHeap(
n
);
for
(
int
i = 0; i <
n
; i++) pushHeap(h,
node_arr
[i]);
for
(
int
i = 1; i <
n
; i++) {
Node
*node1 = top(h);
popHeap(h);
Node
*node2 = top(h);
popHeap(h);
Node
*node3 = getNewNode(node1->freq + node2->freq, 0);
node3->lchild = node1;
node3->rchild = node2;
pushHeap(h, node3);
}
Node
*ret = top(h);
clearHeap(h);
return
ret;
}
void
clear(
Node
*
root
) {
if
(
root
==
NULL
)
return
;
clear(
root
->lchild);
clear(
root
->rchild);
free(
root
);
return
;
}
#define
MAX_CHAR_NUM
128
char
*char_code[
MAX_CHAR_NUM
] = {0};
void
extractHaffmanCode(
Node
*
root
,
char
buff
[],
int
k
) {
buff
[
k
] = 0;
if
(
root
->lchild ==
NULL
&&
root
->rchild ==
NULL
) {
char_code[
root
->ch] = strdup(
buff
);
return
;
}
buff
[
k
] =
'0'
;
extractHaffmanCode(
root
->lchild,
buff
,
k
+ 1);
buff
[
k
] =
'1'
;
extractHaffmanCode(
root
->rchild,
buff
,
k
+ 1);
return
;
}
int
main() {
char
s[10];
int
n, freq;
scanf(
"%d"
, &n);
Node
**node_arr = (
Node
**)malloc(
sizeof
(
Node
*) * n);
for
(
int
i = 0; i < n; i++) {
scanf(
"%s%d"
, s, &freq);
node_arr[i] = getNewNode(freq, s[0]);
}
Node
*root = buildHaffmanTree(node_arr, n);
char
buff[1000];
extractHaffmanCode(root, buff, 0);
for
(
int
i = 0; i <
MAX_CHAR_NUM
; i++) {
if
(char_code[i] ==
NULL
)
continue
;
printf(
"%c : %s\n"
, i, char_code[i]);
}
clear(root);
return
0;
}
对顶堆:
对顶堆是由一个大顶堆和一个小顶堆组合而成的数据结构,与传统堆维护最大数不同,对顶堆用于动态维护第k大的数。在对顶堆中,小根堆位于大根堆的上方,要保证小根堆的所有数始终比大根堆大。
对于对顶堆,我们可以用两个优先队列来表示两个堆。而他所维护的,我们可以看成一个单调的序列。