2)按顺序将两个最小的节点作为叶子节点,小的节点作为左节点,大的作为有节点,相应编码的时候往左为0,往右为1
继续进行下去
package Data.HuffTree.structure;
import java.util.*;
public class testHuffTree{
HuffCodeTree tree = new HuffCodeTree();
public static void main(String[] args){
String str = "cwdav casdc csadcadav ";
testHuffTree huffcode = new testHuffTree();
long start = System.currentTimeMillis();
String encode = huffcode.encode(str);
System.out.println(System.currentTimeMillis() - start);
System.out.println(encode);
System.out.println(huffcode.decode(encode));
}
public String encode(String str) {
char[] chars = str.toCharArray();
// 首先整理一遍所有字符 统计他们出现的频率 也就是个字节点的权重
Map charsInfoMap = countCharFrequence(chars);
// 根据权重由小到大的顺序排列各结点 采用用优先级队列
PriorityQueue nodeQue = arrangeByFre(charsInfoMap);
// 通过这些节点 来创建哈夫曼树 得到所有字符的编码信息
buildHuffCodeTree(nodeQue);
//使用对所有的字符进行编码
return this.tree.encode(chars);
}
public String decode(String str){
return this.tree.decode(str);
}
public Map countCharFrequence(char[] chars){
// 用键值对来存放字符以及其出现的频率
Map charsInfoMap = new Hashtable();
for (char c : chars) {
Character cs = new Character(c);
if(charsInfoMap.containsKey(cs)) {
charsInfoMap.put(cs, charsInfoMap.get(cs) + 1);
} else {
charsInfoMap.put(cs, new Integer(1));
}
}
}
public PriorityQueue arrangeByFre(Map charsInfoMap){
//通过优先队列 有序存放节点 当然节点必须重写compareTo方法
PriorityQueue nodeQue= new PriorityQueue<>(charsInfoMap.size());
for (Character key : charsInfoMap.keySet()) {
Node node = new Node(key.toString(), charsInfoMap.get(key));
nodeQue.add(node);
this.tree.addleaf(node);
}
return nodeQue;
}
public void buildHuffCodeTree(PriorityQueue nodeQue){
if(nodeQue == null || nodeQue.size() == 0){
return;
}
//构造哈夫曼树 小的节点为左子树 大的节点为右
while(nodeQue.size() > 1){
Node node1 = nodeQue.poll();
Node node2 = nodeQue.poll();
Node parent = new Node(node1.chars + node2.chars,
node1.frequence + node2.frequence);
parent.leftChild = node1;
parent.rightChild = node2;
node1.parentChild = parent;
node2.parentChild = parent;
nodeQue.add(parent);
}
this.tree.root = nodeQue.poll();
}
}
class HuffCodeTree{
Node root = null;
/**
* 所有叶子节点 即所有进行编码的字符所在的节点
* */
public List leaves = new LinkedList<>();
public String encode(char[] chars){
if(chars.equals("")){return "";}
// 对所有出现的字符进行编码
Map chara_encoding = new HashMap<>();
for(Node leaf : this.leaves){
Node node = leaf;
String charcode = "";
while(node != root) {
if (node.isLeftChild()) {
charcode = '0' + charcode;
} else {
charcode = '1' + charcode;
}
node = node.parentChild;
}
// System.out.print(leaf.chars+ " : " + charcode + " ");
chara_encoding.put(leaf.chars.charAt(0), charcode);
}
StringBuffer code = new StringBuffer();
for(char c : chars){
code.append(chara_encoding.get(c));
}
return code.toString();
}
public void addleaf(Node e){
Node node = e;
this.leaves.add(node);
}
public void preOrderTra(){
preOrderTra(root);
}
//递归 先序遍历
public void preOrderTra(Node localRoot) {
if(localRoot != null) {
localRoot.displayNode();
preOrderTra(localRoot.leftChild);
preOrderTra(localRoot.rightChild);
}
}
public String decode(String str){
if(root == null){
return null;
}
String deocodeString = "";
Node node = root;
for(char c : str.toCharArray()){
if(node.leftChild == null && node.rightChild == null){
deocodeString += node.chars;
node = root;
}
if(c == '0'){
node = node.leftChild;
}else if(c == '1') {
node = node.rightChild;
}
}
return deocodeString;
}
}
class Node implements Comparable{
public String chars = "";
public int frequence;
public Node leftChild;
public Node rightChild;
public Node parentChild;
public Node(String str, int fre){
this.chars = str;
this.frequence = fre;
}
public void displayNode(){
System.out.println("节点: " + chars + " " + frequence);
}
public boolean isLeftChild(){
return this.parentChild != null && this.parentChild.leftChild == this;
}
@Override
public int compareTo(Node o) {
return this.frequence - o.frequence;
}
}
对编码与解码的思路是通过参考如下网页的,只是自己将不同部分的功能分配到了不同的类中,对集合的使用也是参照的,其使用和自己对代码的修正,大大的调整了编码的效率。
参考:https://blog.csdn.net/kimylrong/article/details/17022319
输入:需编码的n个字符以及对应的权重
得到:字符对应的01编码
思路:
* 使用【2 * n - 1】个节点数组,存放树中所有节点,前n个是叶子节点,也即对应各个字符。
* 每个节点保存的信息:父节点,左叶子节点,右叶子节点,权重
* 没有父节点的视作当前字数根节点,没有叶子节点位即为编码字符对应的一个节点,可知当树完全构造成功时,数组最后一个节点即为树的根节点
* 通过数组保存节点,且在构造哈夫曼树时,不断通过寻找数组中前两个既没有父节点又是最小权重的两个节点,构造下一个子树,会发现因为每次权重最小的节点总是最接近数组最前面,因此保证最后构造的哈夫曼树时最矮的(或者说是最满的)
// CHuffman.h
#include
#include
using namespace std;
class CHuffman
{
private:
typedef struct {
int weight;
int parent;
int lchild;
int rchild;
}HuffmanNode, *HuffmanTree;
HuffmanNode* nodes;
string* codes;
int nodeNum;
string* chars;
public:
CHuffman(); // 编码的字符, 字符的权重
~CHuffman();
void PrintAllCodes();
void Encode(string codes[], int a[], int n);
string Decode(string s);
private:
void CreateHuffTree(int a[], int n); // 构造哈夫曼树
void InitNode(int i, int weight = 0, int parent = -1, int lchild = -1, int rchild = -1); // 初始化一个节点
void Select(HuffmanNode* noeds, int range, int& n1, int& n2); // 选出权重最小的两个节点的序号
void GetHuffcodes(); // 计算所有的0, 1哈夫曼编码
};
// CHuffman.cpp
#include "CHuffman.h"
CHuffman::CHuffman() {
nodes = NULL;
nodeNum = NULL;
chars = NULL;
}
void CHuffman::Encode(string codes[], int a[], int n) {
chars = codes;
CreateHuffTree(a, n);
GetHuffcodes();
}
void CHuffman::CreateHuffTree(int a[], int n) {
// 使用数组存放整个树的节点, 共有2 * n - 1 个节点
nodeNum = n;
int m = 2 * n - 1;
nodes = new HuffmanNode[m];
// 前n个节点存放叶子节点
for (int i = 0; i < n; i++) {
InitNode(i, a[i]);
}
for (int i = n; i < m; i++) {
InitNode(i, 0, -1);
}
// 构造Huffman树
for (int i = n; i < m; i++) {
int n1, n2; // 当前无父节点的所有节点中,权重最小的两个节点的序号
Select(nodes, i, n1, n2);
nodes[n1].parent = i;
nodes[n2].parent = i;
InitNode(i, nodes[n1].weight + nodes[n2].weight, -1, n1, n2);
}
}
void CHuffman::GetHuffcodes() {
int n = nodeNum;
codes = new string[n];
for (int i = 0; i < n; i++) {
int cur = i;
codes[i] = "";
int parent;
while ((parent = nodes[cur].parent) != -1) {
cout << cur << " parent" << parent << ": " << nodes[parent].lchild << " " << nodes[parent].rchild << " \n";
if (nodes[parent].lchild == cur) {
codes[i] = '0' + codes[i];
}
else {
codes[i] = '1' + codes[i];
}
cur = parent;
}
}
}
void CHuffman::InitNode(int i, int weight, int parent, int lchild, int rchild)
{
nodes[i].weight = weight;
nodes[i].parent = parent;
nodes[i].lchild = lchild;
nodes[i].rchild = rchild;
}
void CHuffman::Select(HuffmanNode* noeds, int range, int& n1, int& n2) {
// 找到两个没有父节点的节点位置
n1 = range, n2 = range;
// 找到最小权重节点
for (int i = 0; i < range; i++) {
if (nodes[i].parent == -1 ){
if (n1 == range) {
n1 = i;
}
else {
if (nodes[i].weight < nodes[n1].weight) {
n1 = i;
}
}
}
}
// 找到第二小权重节点
for (int i = 0; i < range; i++) {
if (nodes[i].parent == -1 && i != n1){
if (n2 == range) {
n2 = i;
}
else {
if ( nodes[i].weight < nodes[n2].weight) {
n2 = i;
}
}
}
}
}
void CHuffman::PrintAllCodes() {
cout << "所有的编码序列为:\n";
for (int i = 0; i < nodeNum; i++) {
cout << chars[i] << ": " << codes[i] << "\n";
}
}
string CHuffman::Decode(string s) {
// 解码01串
// 在此出直接将权重作为最后的解码值
cout << "解码";
int curNode = 2 * nodeNum - 2;
if (s.length() < 1) {
return "";
}
string codes = "";
for (int i = 0; i < s.length(); i++) {
char curChar = s.at(i);
if (curChar == '0') {
curNode =nodes[curNode].lchild;
}
else if (curChar == '1') {
curNode = nodes[curNode].rchild;
}
else {
exit;
}
if (nodes[curNode].lchild == -1 && nodes[curNode].rchild == -1) {
codes += chars[curNode];
curNode = 2 * nodeNum - 2;
}
}
return codes;
}
CHuffman::~CHuffman() {
delete[] nodes;
delete[] codes;
chars = nullptr;
}
// main.cpp
#include "CHuffman.h"
#include
using namespace std;
int main()
{
string s[] = { "a", "c", "d", "e", "f", "s" };
int a[] = { 5, 2, 1, 1, 3, 2 };
CHuffman tree;
tree.Encode(s, a, 6);
tree.PrintAllCodes();
cout << tree.Decode("000001010110000011");
}
代码、以及代码规范还有很多问题,请查看的童鞋多多指正,谢谢!