哈夫曼树介绍:
哈夫曼树介绍https://zhuanlan.zhihu.com/p/54714101
哈夫曼树是一类带权路径最短的树。构造这种树的算法最早由哈夫曼提出,这种树在信息检索中很有用。如用于通讯及数据传送中构造传输效率最高的二进制编码(哈夫曼编码),用于编程中构造平均执行时间最短的最佳判断过程。
思路:
用一个Map<序号,子树树根> treeMap,去存储所有的子树。
用一个List<int> arr去存储从小到大排序的数字与子树。
arr中的排序组合有四种:数字与数字、数字与子树、子树与数字、子树与子树。
写这出对应这四种情况的函数,用for循环与执行,知道arr中只有一个元素,这个元素就是我们想要的哈夫曼树。
例如:
- 3 5 7 8 11 14 23 29
- 7 8 -8 11 14 23 29
- -8 11 14 -15 23 29
- 14 -15 -19 23 29
- -19 23 29 -29
- 29 -29 -42
- -58 -42
- -100
计算某个节点的哈夫曼编码,从根节点想左为0,向右为1。
#define _CRT_SECURE_NO_WARNINGS
#include <map>
#include <cmath>
#include <vector>
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
typedef struct binaryTree {
int node = -1;
struct binaryTree* lchild = NULL, * rchild = NULL;
}Tree, * TreePointer;
void inputElement();
bool compare(int a, int b);
TreePointer myPopArrEleToTree();
int myPopArr();
TreePointer myPopMap(int iindex);
void createSubTree();
void joinNumToSubTree();
void joinSubTreeToNum();
void joinSubTreeToSubTree();
void xin(TreePointer& T);
vector<int> arr; //用于存储当前所有元素,永远是按绝对值从小到大排序,正常元素为正数,如果是子树为负数 如队列{2,-3 5 -6 7.8}
map<int, TreePointer> treeMap; //用于存放子树
int main(void) {
inputElement();
while (arr.size()>2) { //运行到最后只剩两个元素,执行后会生成最后一个树,这个树就是我们要的答案
if (arr.size() >= 2 && arr[0] > 0 && arr[1] > 0)
createSubTree();
//如果两个元素是,一个子树,一个数字
if (arr.size() >= 2 && arr[0] < 0 && arr[1]>0)
joinSubTreeToNum();
if (arr.size() >= 2 && arr[0] > 0 && arr[1] < 0)
joinNumToSubTree();
if (arr.size() >= 2 && arr[0] < 0 && arr[1] < 0)
joinSubTreeToSubTree();
}
cout << "result: ";
for (auto t : treeMap)
xin(t.second);
return 0;
}
//输入元素,输入-1退出
void inputElement() {
int in;
while (1) {
cin >> in;
if (in == -1) break;
arr.push_back(in);
}
sort(arr.begin(), arr.end(), compare);
}
//比较器
bool compare(int a, int b) {
return abs(a) < abs(b);
}
//获取最小的值并且获取后删除,给树赋值后返回
TreePointer myPopArrEleToTree() {
TreePointer T = new Tree;
T->node = arr[0];
arr.erase(arr.begin());
return T;
}
//过去arr的第一个元素,并且删除第一个元素
int myPopArr() {
int i = arr[0];
arr.erase(arr.begin());
return i;
}
//获取treeMap中指定的子树,在treeMap中删除这个子树,并返回这个子树
TreePointer myPopMap(int index) {
TreePointer T = treeMap[index];
treeMap.erase(index);
return T;
}
//添加元素
void addToArr(int i) {
arr.push_back(i);
sort(arr.begin(), arr.end(), compare);
}
//挨着的如果是两个数字则创建一个子树,并放到treeMap中
void createSubTree() {
TreePointer T = new Tree;
T->lchild = myPopArrEleToTree();
T->rchild = myPopArrEleToTree();
treeMap[-(T->lchild->node + T->rchild->node)] = T;
addToArr(-(T->lchild->node + T->rchild->node));
}
//挨着的如果是一个子树与数字创建一个子树,并放到treeMap中
void joinSubTreeToNum() {
TreePointer T = new Tree;
int weight = myPopArr();
T->lchild = myPopMap(weight);
T->rchild = myPopArrEleToTree();
treeMap[-(-weight + T->rchild->node)] = T;
addToArr(-(-weight + T->rchild->node));
}
//挨着的如果是一个数字与子树创建一个子树,并放到treeMap中
void joinNumToSubTree() {
TreePointer T = new Tree;
T->rchild = myPopArrEleToTree();
int weight = myPopArr();
T->lchild = myPopMap(weight);
treeMap[-(-weight + T->rchild->node)] = T;
addToArr(-(-weight + T->rchild->node));
}
//挨着的如果是一个子树与子树创建一个子树,并放到treeMap中
void joinSubTreeToSubTree() {
TreePointer T = new Tree;
int weight1 = myPopArr(), weight2 = myPopArr();
T->lchild = myPopMap(weight1);
T->rchild = myPopMap(weight2);
treeMap[-(-weight1 + (-weight2))] = T;
addToArr(-(-weight1 + (-weight2)));
}
string str = "";
void xin(TreePointer& T) {
if (T) {
if (T->node != -1)
cout << str << " ";
str += "0";
xin(T->lchild);
str = str.substr(0, str.size() - 1);
str += "1";
xin(T->rchild);
str = str.substr(0, str.size() - 1);
}
}