持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情
手把手教学考研大纲范围内树定义,遍历,Huffman,并查集 22考研大纲数据结构要求的是C/C++,笔者以前使用的都是Java,对于C++还很欠缺, 如有什么建议或者不足欢迎大佬评论区或者私信指出 初心是用最简单的语言描述数据结构
Talk is cheap. Show me the code. 理论到处都有,代码加例题自己练习才能真的学会
Huffman树+编码
#include "iostream"
#include "queue"
#include "unordered_map"
#include "math.h"
#include "string"
#include "string.h"
using namespace std;
typedef struct TreeNode { //定义Huffman树结构体:字符,权重,huffman编码,左子结点,右子结点
char c;
int weight;
char huffman_code[100];
struct TreeNode *lefttree;
struct TreeNode *righttree;
}TreeNode, *Tree;
struct cmp{ //定义树结点排序规则,按权重从小到大排序
bool operator()(Tree a, Tree b) {
return a->weight > b->weight;
}
};
//huffman树的初始化
void huffmanTreeInit(Tree &tree,unordered_map<char, int> words) { //用unordered_map保存每个字符出现的次数
//map和unordered_map差不多的,map是顺序的,unordered_map是按照hash顺序的
//!!!优先队列,符合排序的顺序,但是调试的时候里面的顺序不是排序的,弹出的时候是按照排序后的顺序弹出的
//!!!优先队列,底层为堆排序,是存储找vector或deque里面,所以调试的时候看到的不是排好序的
priority_queue<Tree, vector<Tree>,cmp> nodes;
unordered_map<char, int> :: iterator words_it; //创建一个unordered_map的迭代器
for (words_it = words.begin(); words_it != words.end(); words_it++) { //循环map里面的内容
Tree temp = new TreeNode; //创建树结点,map保存的字符和数量,赋值给树结点
temp->c = words_it->first;
temp->weight = words_it->second;
temp->lefttree = NULL; //左右子结点赋空值
temp->righttree = NULL;
nodes.push(temp); //把创建的树结点保存到优先队列里
}
//优先队列保证队列内得到元素都是按照自定义的排序规则排序的
while (!nodes.empty()) { //只要优先队列不为空,证明还有结点未访问
if (nodes.size() == 1) { //当剩下一个结点的时候,无需在合并,把这个结点给tree,从队列弹出结点
tree = nodes.top();
nodes.pop();
} else {
Tree temp1 = nodes.top(); //取出权重小的两个结点
nodes.pop();
Tree temp2 = nodes.top();
nodes.pop();
Tree newtemp = new TreeNode; //把两个权重小的结点合成一个结点
newtemp->weight = temp1->weight + temp2->weight;
if (temp1->weight < temp2->weight) { //权重小的放到左子结点
newtemp->lefttree = temp1;
newtemp->righttree = temp2;
} else {
newtemp->lefttree = temp2;
newtemp->righttree = temp1;
}
newtemp->c = '0'; //非叶子结点字符赋 0
nodes.push(newtemp); //把新结点入优先队列
}
}
}
//huffman编码生成 0代表左子结点,1代表右子结点
void huffmanCodeInit(Tree &tree) { //用层序遍历,每一层huffman编码多一位
Tree temp = tree;
queue<Tree> nodes;
nodes.push(temp);
while (!nodes.empty()) {
temp = nodes.front(); //访问到当前结点,把当前结点从队列弹出
nodes.pop();
if (temp->lefttree != NULL) { //左子结点不为空,就把左子结点放入优先队列,
nodes.push(temp->lefttree);
strcpy(temp->lefttree->huffman_code, temp->huffman_code); //左结点huffman编码=父结点huffman编码 + 0
char *str = temp->lefttree->huffman_code;
while (*str != '\0') {
*str++;
}
*str = '0';
}
if (temp->righttree != NULL) { //与左子结点同理
nodes.push(temp->righttree);
strcpy(temp->righttree->huffman_code, temp->huffman_code);
char *str = temp->righttree->huffman_code;
while (*str != '\0') {
*str++;
}
*str = '1';
}
}
}
//打印huffman编码
void huffmanCodePrint(Tree tree) {
priority_queue<Tree, vector<Tree>, cmp> nodes;
queue<Tree> queue;
queue.push(tree);
while (!queue.empty()) { //先层序遍历,把每个结点都保存到优先队列里面
Tree temp = queue.front();
queue.pop();
if (temp->c != '0') { //如果字符是 '0' 此结点为非叶子结点,并不是真正的huffman结点
nodes.push(temp);
}
if (temp->lefttree != NULL) {
queue.push(temp->lefttree);
}
if (temp->righttree != NULL) {
queue.push(temp->righttree);
}
}
while (!nodes.empty()) { //优先队列(保证队列是有序的) 有序输出每个树结点
Tree t = nodes.top();
nodes.pop();
cout << "字符: " << t->c << " 权值: " << t->weight << " 编码: " << t->huffman_code << endl;
}
}
//得到当前huffman树的深度
int getDepth(Tree tree) {
if (tree == NULL) {
return 0;
}
return max(getDepth(tree->lefttree), getDepth(tree->righttree)) + 1;
}
//格式化打印huffman树 和链式存储二叉树的格式化输出差不多
void huffmanTreeTypePrint(Tree tree) {
cout << "打印二叉树: 两行为一层,第一层为字符,第二层为权值 \n";
cout << " 第一层字符: 字符为 0 说明下面有结点, 字符为 N 说明为空结点 \n";
cout << " 第一层权值: 权值为 0 \n" << endl;
int depth = getDepth(tree);
queue<Tree> q;
q.push(tree);
for (int i = 0; i < depth; i++) {
int space = 0;
for (int j = 0; j < depth - 1 - i; j++) {
space = space * 2 + 1;
}
for (int j = 0; j < space; j++) { //输出huffman字符
cout << " ";
}
queue<Tree> temp = q;
for (int j = 0; j < pow(2, i); j++) {
cout << q.front()->c;
if (q.front()->lefttree != NULL) {
q.push(q.front()->lefttree);
} else {
TreeNode *t = new TreeNode;
t->c = 'N';
t->lefttree = NULL;
t->righttree = NULL;
q.push(t);
}
if (q.front()->righttree != NULL) {
q.push(q.front()->righttree);
} else {
TreeNode *t = new TreeNode;
t->c = 'N';
t->lefttree = NULL;
t->righttree = NULL;
q.push(t);
}
q.pop();
for (int k = 0; k < space * 2 + 1; k++) {
cout << " ";
}
}
cout << "\n";
for (int j = 0; j < space; j++) { //输出huffman权值
cout << " ";
}
for (int j = 0; j < pow(2, i); j++) {
cout << temp.front()->weight;
if (temp.front()->lefttree != NULL) {
temp.push(temp.front()->lefttree);
} else {
TreeNode *t = new TreeNode;
t->weight = 0;
t->lefttree = NULL;
t->righttree = NULL;
temp.push(t);
}
if (temp.front()->righttree != NULL) {
temp.push(temp.front()->righttree);
} else {
TreeNode *t = new TreeNode;
t->weight = 0;
t->lefttree = NULL;
t->righttree = NULL;
temp.push(t);
}
temp.pop();
for (int k = 0; k < space * 2 + 1; k++) {
cout << " ";
}
}
cout << "\n";
}
cout << endl;
}
int main() {
Tree tree;
unordered_map<char, int> words; //用来存每个字符以及字符出现的个数
string s;
cin >> s;
unordered_map<char, int> :: iterator words_it; //定义map迭代器,用来表示map的某个结点
for (int i = 0; i < s.length(); i++) {
words_it = words.find(s[i]); //找到s[i]字符的结点
if (words_it == words.end()) { //如果找到map结尾,说明不存在当前字符的结点
words.insert(make_pair(s[i], 1)); //添加当前字符,当前字符出现的次数为1
} else { //如果找到了,就把当前字符出现的次数 + 1
words_it->second++;
}
}
//构建huffman树
huffmanTreeInit(tree, words);
//构建完成 huffman树,构建huffman编码
huffmanCodeInit(tree);
//格式化输出huffman树
huffmanTreeTypePrint(tree);
//输出huffman编码
huffmanCodePrint(tree);
return 0;
}