树形结构是计算机最重要的数据结构,链表示特殊的树.
特点:
- 节点抽象为集合,边抽象为关系。
- 树由节点与边构成。
- 子节点之间是没有交集的。
- 每个节点的指针域两个至多个(N叉树)。
- 节点数 = 边数 + 1。
- 图的度 = 出度 + 入度, 树的度 = 出度。
定理:
二叉树中度数为零的节点比度数为2的节点多一个。
满二叉树:没有度数为1的节点。
完全二叉树:除了最后一层的所有节点度数均为2。
树的遍历种类:
- 先序遍历:1. 根节点 2. 左子树 3. 右子树
- 中序遍历:1. 左子树 2. 根节点 3. 右子树
- 后序遍历:1. 左子树 2. 右子树 3. 根节点
巧记:所谓前中后序,是根据根节点的位置的访问时序决定的。
哈夫曼树 :
哈夫曼编码用于数据压缩。
例子:具有三个字母的系统
若采用定长编码:
每个单元需要两个比特位,期望为2。
若采用变长编码:
根据a, b, c出现频率为 a:0.5 b:0.4 c:0.1,将a,b,c编码为0, 10, 11,则期望为1.5
哈夫曼树是一颗满二叉树,n个叶子节点, n - 1个中间节点(度数为2)。
至今为止人类发现的最优的变长编码(离线编码)。
思考与分析:
具有n个字符的系统。
他们出现的概率分别是
a1,a2,a3…an
a
1
,
a
2
,
a
3
…
a
n
对应的比特长度为
l1,l2,l3…ln
l
1
,
l
2
,
l
3
…
l
n
。
lsum=∑ni=1ai∗li l s u m = ∑ i = 1 n a i ∗ l i
我们最终的目标是要实现期望 lsum l s u m 达到最小,那么这也就说我们需要做到 amax a m a x 对应 lmin l m i n 。
深度大的说明他的编码长度长,那么该分配他出现概率低的字符
构建步骤:
- 首先找出出现频率最低的两个字符为他们分配前缀,然后进行合并。
- 接下来为树中每一条边进行赋值
- 不断合并最终仅剩一个根节点
演示代码:
二叉树:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int key;
struct Node *lchild, *rchild;
} Node;
Node *getNode(int);
void clear(Node *);
void pre_order(Node *);
void in_order(Node *);
void post_order(Node *);
int main(){
Node *root = getNode(1);
root->lchild = getNode(2);
root->rchild = getNode(3);
root->lchild->lchild = getNode(7);
root->lchild->rchild = getNode(8);
root->lchild->rchild->lchild = getNode(9);
root->rchild->rchild = getNode(4);
root->rchild->rchild->lchild = getNode(5);
root->rchild->rchild->rchild = getNode(6);
pre_order(root); printf("\n");
in_order(root); printf("\n");
post_order(root); printf("\n");
}
Node *getNode(int key) {
Node *p = (Node *)malloc(sizeof(Node));
p->key = key;
p->lchild = p->rchild = NULL;
return p;
}
void clear(Node *root) {
if (root == NULL) return;
clear(root->lchild);
clear(root->rchild);
free(root);
return;
}
void pre_order(Node* root) {
if (root == NULL) return;
printf("%d ", root->key);
pre_order(root->lchild);
pre_order(root->rchild);
}
void in_order(Node *root) {
if (root == NULL) return;
in_order(root->lchild);
printf("%d ", root->key);
in_order(root->rchild);
}
void post_order(Node *root) {
if (root == NULL) return;
post_order(root->lchild);
post_order(root->rchild);
printf("%d ", root->key);
}
哈夫曼树:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CHAR_NUM 26
#define swap(a, b) { \
__typeof(a) temp; \
temp = a; \
a = b; \
b = temp; \
}
typedef struct HFNode {
char ch;
int freq;
struct HFNode *lchild, *rchild;
} HFNode;
HFNode *getNode() {
HFNode *p = (HFNode *)malloc(sizeof(HFNode));
p->freq = p->ch = 0;
p->lchild = p->rchild = NULL;
return p;
}
void build(int n, HFNode *arr[]) {
for (int times = 0; times < n - 1; times++) {
HFNode *minNode = arr[0];
int ind = 0;
for (int i = 1; i < n - times; i++) {
if (arr[i]->freq >= minNode->freq) continue;
minNode = arr[i];
ind = i;
}
swap(arr[ind], arr[n - times - 1]);
minNode = arr[0];
ind = 0;
for (int i = 1; i < n - times - 1; i++) {
if (arr[i]->freq >= minNode->freq) continue;
minNode = arr[i];
ind = i;
}
swap(arr[ind], arr[n - times - 2]);
HFNode *new_node = getNode();
new_node->lchild = arr[n - times - 1];
new_node->rchild = arr[n - times - 2];
new_node->freq = arr[n - times - 1]->freq + arr[n - times - 2]->freq;
arr[n - times - 2] = new_node;
}
return ;
}
void extract(HFNode *root, char *buff, char (*huffman_code)[100], int n) {
buff[n] = '\0';
if (root->lchild == NULL && root->rchild == NULL) {
strcpy(huffman_code[root->ch], buff);
return;
}
buff[n] = '0';
extract(root->lchild, buff, huffman_code, n + 1);
buff[n] = '1';
extract(root->rchild, buff, huffman_code, n + 1);
return ;
}
int main() {
HFNode *arr[CHAR_NUM] = {0};
char buff[100];
char huffman_code[256][100] = {0};
int freq;
for (int i = 0; i < CHAR_NUM; i++) {
scanf("%s%d", buff, &freq);
printf("read %s = %d\n", buff, freq);
HFNode *new_node = getNode();
new_node->ch = buff[0];
new_node->freq = freq;
arr[i] = new_node;
}
build(CHAR_NUM, arr);
extract(arr[0], buff, huffman_code, 0);
for (int i = 0; i < 256; i++) {
if (huffman_code[i][0] == 0) continue;
printf("%c : %s\n", (char)i, huffman_code[i]);
}
return 0;
}