下面主要编码实现关于树结构的两种应用-最大堆和哈夫曼编码。
一、最大堆:根结点是整棵树的最大值的完全二叉树,这种结构在排序等功能中应用的比较广,类似的结构有最小堆。
在MaxHeap.h:
#include"iostream"
#define ElementType int
using namespace std;
class MaxHeap
{
public:
MaxHeap();
~MaxHeap();
void CreatMaxHeap(int Maxsize, int MaxData);
void Insert(ElementType item);
ElementType DeleteMax();
bool IsFull();
bool IsEmpty();
private:
ElementType * Elements; //存储堆元素
int Size;
int Capactiy;
};
在MaxHeap.cpp中:
#include "MaxHeap.h"
MaxHeap::MaxHeap()
{
Elements = (ElementType *)malloc(sizeof(ElementType));
}
MaxHeap::~MaxHeap()
{
}
void MaxHeap::CreatMaxHeap(int MaxSize, int MaxData) //初始化一个有根结点的最大堆
{
Size = 1;
Capactiy = MaxSize;
Elements[0] = Elements[1] = MaxData;
}
void MaxHeap::Insert(ElementType item) /* 将元素item 插入最大堆H, 其中H->Elements[0]已经定义为哨兵 */
{
int i;
if (IsFull()) {
cout << "error\n";
return;
}
i = ++Size; /* i指向插入后堆中的最后一个元素的位置 */
for (; Elements[i / 2] < item; i /= 2)
Elements[i] = Elements[i / 2]; /* 向下过滤结点 */
Elements[i] = item; /* 将item 插入 */
}
ElementType MaxHeap::DeleteMax() /* 从最大堆H中取出键值为最大的元素, 并删除一个结点 */
{
int Parent, Child;
ElementType MaxItem, temp;
if (IsEmpty()) {
cout << "error\n";
return -1;
}
MaxItem = Elements[1]; /* 取出根结点最大值 */
/* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */
temp = Elements[Size--];
for (Parent = 1; Parent * 2 <= Size; Parent = Child) {
Child = Parent * 2;
if ((Child != Size) &&
(Elements[Child] < Elements[Child + 1]))
Child++; /* Child指向左右子结点的较大者 */
if (temp >= Elements[Child]) break;
else /* 移动temp元素到下一层 */
Elements[Parent] = Elements[Child];
}
Elements[Parent] = temp;
return MaxItem;
}
bool MaxHeap::IsFull()
{
if (Size >= Capactiy)
return 1;
else
return 0;
}
bool MaxHeap::IsEmpty()
{
if (Size == 0)
return 1;
else
return 0;
}
在main.cpp:
#include"MaxHeap.h"
int main()
{
MaxHeap H;
H.CreatMaxHeap(10, 58);
H.Insert(25);
H.Insert(44);
cout << H.DeleteMax() << "\n";
H.Insert(22);
cout << H.DeleteMax() << "\n";
system("pause");
return 0;
}
二、哈夫曼编码:采用的是哈夫曼数(又称最优二叉树)存储数据
在huffmanTree.h中:
#pragma once
#include"iostream"
using namespace std;
typedef char* HuffmanCode;
typedef struct HTNode *HuffmanTree;
struct HTNode
{
int weight;
int parent, leftchild, rightchild;
};
void SelectMin(HuffmanTree HT, int nNode, int s1, int s2);
void HuffmanCoding(HuffmanTree &HT, HuffmanCode HC[], int *w, int nNode);
在HuffmanTree.cpp中:
/*哈夫曼树又称最优二叉树:带权路径长度(WPL)最小的二叉树
1.从原始元素集合T中拿出两个频度最小的元素组成一个二叉树,二叉树的根为这两个节点频度的和。
2.然后从集合T中删除这两个元素,把根元素加入到T集合中,如此反复直集合T为空*/
#include"string.h"
#include"HuffmanTree.h"
//选出weight最小的两个结点,s1保存最小的,s2保存第二小的
void SelectMin(HuffmanTree HT, int nNode,int s1,int s2)
{
int i, j;
for (i = 1; i <= nNode; i++)
if (!HT[i].parent)
{
s1 = i;
break;
}
for (j = i + 1; j <= nNode; j++)
if (!HT[j].parent)
{
s2 = j;
break;
}
for (i = 1; i <= nNode; i++)
if ((HT[i].weight < HT[s1].weight) && (!HT[i].parent) && (s2 != i))
s1 = i;
for (j = 1; j <= nNode; j++)
if ((HT[j].weight < HT[s2].weight) && (!HT[j].parent) && (s1 != j))
s2 = j;
// 以上只筛选出最小的两个,这里保证s1的weight比s2的小
if (HT[s1].weight > HT[s2].weight)
{
int tmp = s1;
s1 = s2;
s2 = tmp;
}
}
// w[]存放nNode个字符的权值(均大于0),构造哈夫曼树HT,
// 并求出nNode个字符的哈夫曼编码HC
void HuffmanCoding(HuffmanTree &HT ,HuffmanCode HC[], int *w, int nNode)
{
int i, j,m,s1=0,s2=0;
char *hfcode;
int p;
int cdlen;
if (nNode < 1)
return;
m = 2 * nNode - 1; //哈夫曼树的结点数
/////////////////////////////以下是求Huffman树的初始化/////////////////////////////
HT = (HTNode*)malloc((m + 1) * sizeof(HTNode)); //0号单元未用
for (i = 1; i <= nNode; i++) //初始化
{
HT[i].weight = w[i - 1];
HT[i].parent = 0;
HT[i].leftchild = 0;
HT[i].rightchild = 0;
}
for (i = nNode + 1; i <= m; i++)
{
HT[i].weight = 0;
HT[i].parent = 0;
HT[i].leftchild = 0;
HT[i].rightchild = 0;
}
cout << "weight parent lchild rchild:\n";
for (i = 1; i <= nNode; i++)
cout << i <<" "<< HT[i].weight << " " << HT[i].parent << " " << HT[i].leftchild << " " << HT[i].rightchild;
cout << "****************************************************************\n";
/////////////////////////////以下是求Huffman树的构建/////////////////////////////
for (i = nNode + 1; i <= m; i++)
{
// 建立哈夫曼树
// 在HT[1..i-1]中选择parent为0且weight最小的两个节点
// 其序号分别是s1和s2
SelectMin(HT, i - 1,s1,s2);
HT[s1].parent = i;
HT[s2].parent = i;
cout << "S1 && S2: " << HT[s1].weight << " " << HT[s2].weight << endl;
HT[i].leftchild = s1;
HT[i].rightchild = s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;
/*printf("\nselect: s1 = %d s2 = %d\n", s1, s2);*/
cout << "\nselect: s1 = " << s1 << " s2 = " << s2 << "\n";
/*printf("结点 weight parent lchild rchild");*/
cout << "weight parent lchild rchild:\n";
for (j = 1; j <= i; j++)
cout << j << " " << HT[j].weight << " " << HT[j].parent << " " << HT[j].leftchild << " " << HT[j].rightchild;
cout << "****************************************************************\n";
}
/////////////////////////////以下是求Huffman树的编码/////////////////////////////
// 从根出发
//递归遍历哈夫曼树,求哈夫曼编码
hfcode = (char *)malloc(nNode * sizeof(char)); //分配求编码的工作空间
p = m;
cdlen = 0;
for (i = 1; i <= m; i++)
HT[i].weight = 0; //遍历哈夫曼树时用作结点状态的标志
while (p) //退出条件:p = 结点m的parent,即为0
{
if (HT[p].weight == 0) //向左
{
HT[p].weight = 1;
if (HT[p].leftchild != 0)
{
p = HT[p].leftchild;
hfcode[cdlen++] = '0';
}
else if (HT[p].rightchild == 0)
{
HC[p] = (char *)malloc((cdlen + 1) * sizeof(char));
hfcode[cdlen] = '\0'; //保证后面的不会被复制
strcpy_s(HC[p], strlen(hfcode) + 1, hfcode);
}
}
else if (HT[p].weight == 1) //向右
{
HT[p].weight = 2;
if (HT[p].rightchild != 0)
{
p = HT[p].rightchild;
hfcode[cdlen++] = '1';
}
}
else
{
HT[p].weight = 0;
p = HT[p].parent;
--cdlen;
}
}
}
在main.cpp中:
#include"HuffmanTree.h"
int main()
{
HuffmanTree HT; // 哈夫曼树
HuffmanCode *HC; // 保存哈夫曼编码
int *w, nNode, i; // w记录权值
cout << "Input nNode\n";
cin >> nNode;
HC = (HuffmanCode *)malloc(nNode * sizeof(HuffmanCode));
w = (int *)malloc(nNode * sizeof(int));
cout << nNode << " of weight";
for (i = 0; i < nNode; i++)
cin >> w[i];
HuffmanCoding(HT, HC, w, nNode);
cout << "HuffmanTree of nNode\n";
for (i = 1; i <= nNode; i++)
cout << i << " " << w[i - 1] << " " << HC[i] << "\n";
system("pause");
return 0;
}