樹與二叉樹
阿新 • • 發佈:2019-01-03
樹形結構是計算機最重要的資料結構,連結串列示特殊的樹.
特點:
- 節點抽象為集合,邊抽象為關係。
- 樹由節點與邊構成。
- 子節點之間是沒有交集的。
- 每個節點的指標域兩個至多個(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個字元的系統。
他們出現的概率分別是 對應的位元長度為 。
我們最終的目標是要實現期望達到最小,那麼這也就說我們需要做到 對應 。
深度大的說明他的編碼長度長,那麼該分配他出現概率低的字元
構建步驟:
- 首先找出出現頻率最低的兩個字元為他們分配字首,然後進行合併。
- 接下來為樹中每一條邊進行賦值
- 不斷合併最終僅剩一個根節點
演示程式碼:
二叉樹:
#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;
}