数据结构之二叉树,赫夫曼树(C++版)

#include <iostream>
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#define MAXLISTSIZE 100 //预设的存储空间最大容量
#define FALSE 0
#define TRUE 1
using namespace std;
typedef char ElemType;

typedef struct BiTNode{
ElemType data;
struct BiTNode *Lchild, *Rchild; //左、右孩子指针
}BiTNode, *BiTree;

typedef struct{
BiTree *base; //存储空间基址
int top; //栈顶指针
int stacksize; //允许的最大存储空间以元素为单位
}Stack;

//Link(0):表示指向左右孩子的指针
//Thread(1):表示指向前驱后继的线索
typedef enum PointerTag{Link, Thread};
//定义指针类型,以 Link 表示指针,Thread 表示线索
typedef struct BiThrNode{
ElemType data;
struct BiThrNode *Lchild, *Rchild; //左右指针
PointerTag LTag, RTag; //左右指针类型标志
}BiThrNode, *BiThrTree;

typedef struct{
char ch;
int weight; //用来存放各个结点的权值
int parent, LChild, RChild; //指向双亲、孩子结点的指针
}HTNode, *HuffmanTree;
typedef char **HuffmanCode; //动态分配数组存储哈夫曼编码

void CreateBiTree(BiTree &T)
{
//在先序遍历二叉树的过程中输入二叉树的"先序字符串",建立根指针为 T的二叉链表存储结构。
//在先序字符串中,字符'#'表示空树,其它字母字符为结点的数据元素
char ch;
ch = getchar();
if(ch=='#') T=NULL; //建空树
else{
T = new BiTNode; //"访问"操作为生成根结点
T->data = ch;
CreateBiTree(T->Lchild); //递归建(遍历)左子树
CreateBiTree(T->Rchild); //递归建(遍历)右子树
}//else
}//CreateBiTree

void InOrder(BiTree T)
{ //中序递归遍历以T为根指针的二叉树并输出二叉树
if(T)
{ //T=NULL时,二叉树为空树,不做任何操作
InOrder(T->Lchild); //中序遍历左子树
cout << T->data; //输出T
InOrder(T->Rchild); //中序遍历右子树
}//if
}

void InitStack(Stack &S)
{
// 构造一个最大存储容量为maxsize的空栈S
int maxsize;
maxsize = MAXLISTSIZE;
S.base = new BiTree[maxsize];
if (!S.base) exit(1); //存储分配失败
S.stacksize = maxsize;
S.top = 0; //空栈中元素个数为0
}

bool Push(Stack &S, BiTree e)
{
// 若栈的存储空间不满,则插入元素 e 为新的栈顶元素,并返回 TRUE;否则返回 FALSE
if (S.top == S.stacksize) //栈已满,无法进行插入
return FALSE;
S.base[S.top] = e; //插入新的元素
++S.top; //栈顶指针后移
return TRUE;
}

bool Pop(Stack &S, BiTree &e)
{
//若栈不空,则删除S的栈顶元素,用 e 返回其值,并返回 TRUE;否则返回 FALSE
if (S.top == 0)
return FALSE;
e = S.base[S.top-1]; //返回非空栈中栈顶元素
--S.top;//栈顶指针前移
return TRUE;
}

int StackLength(Stack S)
{
//返回S的元素个数,即栈的长度。
return S.top;
}

void InOrderTraverse(BiTree T)
{ //中序非递归(采用栈)遍历以T为根指针的二叉树并输出二叉树
BiTree p;
Stack S;
InitStack(S);
p = T;
while(p||StackLength(S))
{
if(p)
{ //根指针进栈,遍历左子树
Push(S, p);
p = p->Lchild;
}
else
{ //根指针退栈,访问根结点,遍历右子树
Pop(S, p);
cout << p->data;
p = p->Rchild;
}
}
}//InOrderTraverse

//创建一棵树
void CreatBiThrTree(BiThrTree &BT)
{
char ch;
ch = getchar();
if(ch=='#') BT=NULL; //建空树
else{
BT = new BiThrNode; //"访问"操作为生成根结点
BT->data = ch;
BT->LTag = Link;
BT->RTag = Link;
CreatBiThrTree(BT->Lchild); //递归建(遍历)左子树
CreatBiThrTree(BT->Rchild); //递归建(遍历)右子树
}
}

void InThreading(BiThrTree p, BiThrTree &pre)
{ //对 p 指向根结点的二叉树进行中序遍历,遍历过程中进行"中序线索化"。
//若 p 所指结点的左指针为空,则将它改为"左线索",
//若 pre 所指结点的右指针为空,则将它改为"右线索"。
//指针 pre 在遍历过程中紧随其后,即始终指向 p 所指结点在中序序列中的前驱。
if(p){
InThreading(p->Lchild, pre); //对左子树进行线索化
if(!p->Lchild)
{
p->LTag = Thread;
p->Lchild = pre;
} //建前驱线索
if(!pre->Rchild)
{
pre->RTag = Thread;
pre->Rchild = p;
} //建后继线索
pre = p; //保持 pre 指向 p 的前驱
InThreading(p->Rchild, pre); //对右子树进行线索化
}//if
}//InThreading

//中序遍历线索化
void InOrderThreading(BiThrTree &Thrt, BiThrTree BT)
{
//BT为指向二叉树根结点的指针,由此二叉链表建立二叉树的中序线索链表,Thrt指向线索链表中的头结点。
BiThrTree pre;
if(!(Thrt = new BiThrNode))
exit(1); //存储分配失败
Thrt->LTag = Link;
Thrt->RTag =Thread; //建头结点
Thrt->Rchild = Thrt; //右指针回指
if(!BT)
Thrt->Lchild = Thrt; //若二叉树空,则左指针回指
else{
Thrt->Lchild = BT;
pre = Thrt;
InThreading(BT, pre); //中序遍历进行中序线索化
pre->Rchild = Thrt;
pre->RTag = Thread; //对中序序列中最后一个结点进行线索化
Thrt->Rchild = pre; //建非空树的头结点的"右线索"
}//else
}//InOrderThreading

//中序遍历并输出线索二叉树
void InOrderTraverse_Thr(BiThrTree T)
{ //T指向中序线索链表中的头结点,头结点的左指针 Lchild 指向二叉树的根结点,
//头结点的右线索 Rchild 指向中序遍历访问的最后一个结点。
//本算法对此二叉树进行中序遍历并输出线索二叉树
BiThrTree p;
p = T->Lchild; //p指向二叉树的根结点
while(p!= T)
{ //空树或遍历结束时,p==Thead
while(p->LTag == Link)
p = p->Lchild;
cout << p->data; //访问其左子树为空的结点
while(p->RTag == Thread && p->Rchild != T)
{
p = p->Rchild;
cout << p->data;; //访问"右线索"所指后继结点
}//while
p = p->Rchild; //p进至其右子树根
}//while
}//InOrderTraverse_Thr

void select(HuffmanTree &HT, int a, int &p1, int &p2)
{
int i, j, x, y;
for(j = 1; j <= a; ++j)
{
if(HT[j].parent == 0)
{
x = j;
break;
}
}
for(i = j + 1; i <= a; ++i)
{
if(HT[i].weight < HT[x].weight && !HT[i].parent)
{
x=i; //选出最小的节点
}
}
for(j = 1; j <= a; ++j)
{
if(HT[j].parent==0 && x!=j)
{
y = j;
break;
}
}
for(i = j + 1; i <= a; ++i)
{
if(HT[i].weight < HT[y].weight&&HT[i].parent == 0 && x != i)
{
y = i; //选出次小的节点
}
}
if(x > y)
{
p1 = y;
p2 = x;
}
else
{
p1 = x;
p2 = y;
}
}

void CrtHuffmanTree(HuffmanTree &HT, HuffmanCode &HC, int n)
{ //w存放n个权值, 构造哈夫曼树HT, 并求出哈夫曼编码HC
int i, start, c, p, m, w;
int p1,p2;
char *cd,z;
m = 2 * n - 1;
HT = new HTNode[m+1]; //0号单元未使用
cout << "<<请输入" << n << "个字符和权值>>" << endl;
for(i = 1; i <= n; i++)
{
cout << "请输入第" << i << "字符和权值:";
cin >> z >> w;
HT[i].ch = z;
HT[i].weight = w;
HT[i].parent = 0;
HT[i].LChild = 0;
HT[i].RChild = 0;
}//叶子结点初始化,数组前n个
for(i = n + 1; i <= m; i++)
{
HT[i].ch = '0';
HT[i].weight = 0;
HT[i].parent = 0;
HT[i].LChild = 0;
HT[i].RChild = 0;
}//非叶子结点初始化,数组后n-1个
for(i = n + 1; i <= m; i++) //创建非叶子结点, 建哈夫曼树
{ //在HT[1]~HT[i-1]的范围内选择两个parent为0且weight最小的结点
//其序号分别赋值给p1、 p2返回
select(HT, i-1, p1, p2);
HT[p1].parent = i;
HT[p2].parent = i;
HT[i].LChild = p1;
HT[i].RChild = p2;
HT[i].weight = HT[p1].weight + HT[p2].weight;
}//哈夫曼树建立完毕
//从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码
HC = new char*[n + 1]; //分配n个编码的头指针
cd = new char[n]; //分配求当前编码的工作空间
cd[n - 1] = ''; //从右向左逐位存放编码,首先存放编码结束符
for(i = 1; i <= n; i++) //求n个叶子结点对应的哈夫曼编码
{ start = n - 1; //初始化编码起始指针
for(c = i, p = HT[i].parent; p != 0; c = p, p = HT[p].parent) //从叶子到根结点求编码
if(HT[p].LChild == c)
cd[--start] = '0'; //左分支标0
else
cd[--start] = '1'; //右分支标1
HC[i] = new char[n - start]; //为第i个编码分配空间
strcpy(HC[i], &cd[start]);
}
free(cd);
}

int main()
{
BiTree T;
BiThrTree Thrt, BT;
int i, n;
HuffmanTree HT;
HuffmanCode HC;
cout << "先序输入二叉树('#'表示空树): ";
CreateBiTree(T);
cout << "中序递归遍历输出二叉树为: ";
InOrder(T);
cout << endl;
cout << "中序非递归(采用栈)遍历输出二叉树为: ";
InOrderTraverse(T);
cout << endl;
getchar(); //读取回车符
cout << "先序输入线索二叉树('#'表示空树): ";
CreatBiThrTree(BT);
InOrderThreading(Thrt, BT);
cout << "中序递归遍历输出线索二叉树为: ";
InOrderTraverse_Thr(Thrt);
cout << endl;
cout << "请输入赫夫曼树叶子节点个数:";
cin >> n;
CrtHuffmanTree(HT, HC, n);
for(i = 1; i <= n; i++)
{
cout << HT[i].ch << "的赫夫曼编码为:" << HC[i] << endl;
}
return 0;
}

原文地址:https://www.cnblogs.com/wwttsqt/p/7783201.html