关于哈夫曼树的讲解请参考上篇《三步学通哈夫曼树》(https://blog.csdn.net/helloworldchina/article/details/105210054),这里笔者仅补充一下C#代码的实现。见下:
1 c#代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HuffmanTree
{
class Program
{
public static bool pr = false;//true 打印测试日志
//哈夫曼树的结构
struct HTNode
{
public int weight; // 权值
public int parent, lChild, rChild; // 双亲及左右孩子的下标
}
//输出哈夫曼树各结点信息
static void print(HTNode[] hT)
{
Console.WriteLine("index weight parent lChild rChild");
String str = "";
for (int i = 1, m = hT[0].weight; i <= m; i++)
{
str = String.Format("{0,-7}", i);
Console.Write(str);
str = String.Format("{0,-7}", hT[i].weight);
Console.Write(str);
str = String.Format("{0,-7}", hT[i].parent);
Console.Write(str);
str = String.Format("{0,-7}", hT[i].lChild);
Console.Write(str);
str = String.Format("{0,-7}", hT[i].rChild);
Console.Write(str);
Console.WriteLine();
}
}
// 选择权值最小的两颗树
static int[] selectMin(HTNode[] hT, int k, int index1, int index2)
{
index1 = index2 = 0;
int[] resultInt = new int[2];
int i, j;
for (j = 1; j <= k; j++)
{//使index1,index2分别指向hT中最前面的两个无双亲的结点
if (0 == hT[j].parent)
{
if (0 == index1)
{
index1 = j;
}
else
{
index2 = j;
break;
}
}
}
if (hT[index1].weight > hT[index2].weight)
{//使结点index1的权小于index2的权
int t = index1;
index1 = index2;
index2 = t;
}
for (i = j + 1; i < k; i++)
{//继续查找没有双亲且权值最小的两个结点,将其地址记在index1和index2中
if (0 == hT[i].parent)
{
if (hT[i].weight < hT[index1].weight)
{
index2 = index1;
index1 = i;
}
else if (hT[i].weight < hT[index2].weight)
{
index2 = i;
}
}
}
resultInt[0] = index1;
resultInt[1] = index2;
return resultInt;
}
// 构造有n个权值(叶子结点)的哈夫曼树
static bool createHufmanTree(HTNode[] hT, int n, int k)
{
if (n <= 1) return false;
//给数组赋初值0
for (int i = 0; i <= k; i++)
{
hT[i] = new HTNode();// 如不使用此行代码,执行到hT[i].parent =0时会报空指针异常
hT[i].parent = 0;
hT[i].lChild = 0;
hT[i].rChild = 0;
}
Console.WriteLine("请依次输入权值,并回车确认");
for (int i = 1; i <= n; i++)
{
string inputNumber;
inputNumber = Console.ReadLine();// 输入权值
hT[i].weight = Int32.Parse(inputNumber);
}
hT[0].weight = k; // 用0号结点保存结点数量
if (pr)
{
Console.WriteLine("打印初始的数组:");
print(hT);
}
/****** 初始化完毕, 创建哈夫曼树 ******/
for (int i = n + 1; i <= k; i++)
{
int index1 = -1, index2 = -1;
int[] resultInt = new int[2];
resultInt = selectMin(hT, i, index1, index2);
index1 = resultInt[0];
index2 = resultInt[1];
hT[index1].parent = hT[index2].parent = i;
hT[i].lChild = index1; hT[i].rChild = index2; // 作为新结点的孩子
hT[i].weight = hT[index1].weight + hT[index2].weight; // 新结点为左右孩子结点权值之和
if (pr)
{
Console.WriteLine("打印构建哈夫曼树过程中的数组:");
Console.WriteLine("i=" + i);
Console.WriteLine("index1=" + index1 + ";index2=" + index2);
print(hT);
}
}
return true;
}
static int huffmanTreeWPL_w(HTNode[] hT, int i, int deepth)
{
if (hT[i].lChild == 0 && hT[i].rChild == 0)
{
return hT[i].weight * deepth;
}
else
{
return huffmanTreeWPL_w(hT, hT[i].lChild, deepth + 1) + huffmanTreeWPL_w(hT, hT[i].rChild, deepth + 1);
}
}
// 计算WPL(带权路径长度)
static int huffmanTreeWPL(HTNode[] hT)
{
return huffmanTreeWPL_w(hT, hT[0].weight, 0);
}
static void Main(string[] args)
{
Console.WriteLine("请输入权值的数量n=? ");
int n, k;//n 权值数(叶子结点数) k结点的个数
string inputCount;
inputCount = Console.ReadLine();// 输入权值
n = Int32.Parse(inputCount);
k = 2 * n - 1;
//创建存储哈夫曼树的数组
HTNode[] hT = new HTNode[k + 1];// 0号结点不使用
if (createHufmanTree(hT, n, k))
{
Console.WriteLine("打印哈夫曼树数组:");
print(hT);
Console.WriteLine("WPL = " + huffmanTreeWPL(hT));
}
else Console.WriteLine("输入有误,不能创建哈夫曼树!");
}
}
}
2 运行结果