二叉树的基本操作及哈夫曼编码系统的实现

实验环境:win10,VC++ 6.0  使用语言:C/C++

实验内容一:编写程序,完成二叉树的先序创建、先序遍历、中序遍历和后序遍历等操作

Binary.h

 1 #include<iostream>
 2 #include<stdlib.h>
 3 #include<stack>
 4 #include<queue>
 5 using namespace std;
 6 
 7 typedef char ElemType;
 8 
 9 typedef  struct BiTNode
10 {
11     ElemType data;//数据域
12     struct BiTNode *lchild, *rchild;//左孩子和右孩子
13 }BiTNode, *BiTree;
14 
15 void CreateBiTree(BiTree *T);//创建二叉树,以先序遍历的方式进行输入和创建工作
16 void operation1(ElemType ch);//表示对遍历到的结点数据进行的处理操作,此处操作是将树结点前序遍历输出
17 void operation2(ElemType ch, int level);//此处在输出的基础上,并输出层数
18 void PreOrderTraverse(BiTree T, int level);//递归方式先序遍历二叉树
19 void InOrderTraverse(BiTree T,int level);//递归方式中序遍历二叉树
20 void PostOrderTraverse(BiTree T,int level);//递归方式后序遍历二叉树
21 void PreOrder(BiTree T);//非递归方式先序遍历
22 void InOrder(BiTree T);//非递归中序遍历
23 void PostOrder(BiTree T);//非递归后序遍历
24 void LevelOrder(BiTree T);//层次遍历  

Binary.cpp

#include"Bianry.h"

//根据试验要求,用先序创建的方式进行构建二叉树
//邹老师代课时建议我们加上层次遍历,希望李老师喜欢哈哈
void CreateBiTree(BiTree *T)
{
    ElemType ch;
    cin >> ch;
    if (ch == '#')
        *T = NULL;  
    else
    {
        *T = (BiTree)malloc(sizeof(BiTNode));
        (*T)->data = ch;//生成结点
        CreateBiTree(&(*T)->lchild);//构造左子树
        CreateBiTree(&(*T)->rchild);//构造右子树    
    }
}

//先序遍历
void operation1(ElemType ch)
{
    cout << ch << " ";
}
//此处在输出的基础上,并输出层数
void operation2(ElemType ch, int level)
{
       cout << ch << "在第" << level << "" << "  ";
}


//先序遍历二叉树(递归方式)
void PreOrderTraverse(BiTree T, int level)
{
    if (T == NULL)
        return;
    operation1(T->data);
    //operation2(T->data, level); //可输出层数

    PreOrderTraverse(T->lchild, level + 1);
    PreOrderTraverse(T->rchild, level + 1);
}

//中序遍历二叉树(递归方式)

void InOrderTraverse(BiTree T,int level)
{
if(T==NULL)
return;
InOrderTraverse(T->lchild,level+1);

operation1(T->data);
//operation2(T->data, level); //可输出层数

InOrderTraverse(T->rchild,level+1);
}

//后序遍历二叉树(递归方式)

void PostOrderTraverse(BiTree T,int level)
{
if(T==NULL)
return;
PostOrderTraverse(T->lchild,level+1);
PostOrderTraverse(T->rchild,level+1);

operation1(T->data);
//operation2(T->data, level); //可输出层数
}


/* 思路:将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,再先序遍历T的右子树。*/
void PreOrder(BiTree T){
    stack<BiTree> stack;
    //p是遍历指针  
    BiTree p = T;
    //p不为空或者栈不空时循环
    while (p || !stack.empty())
{
        if (p != NULL)
                 {
            //存入栈中  
            stack.push(p);
            //对树中的结点进行操作
            operation1(p->data);
             //遍历左子树  
            p = p->lchild;
         }
        else
                {
                 //退栈  
            p = stack.top();
            stack.pop();
            //访问右子树  
            p = p->rchild;
        }
    } 
}
//中序遍历(非递归)
void InOrder(BiTree T)
{
    stack<BiTree> stack;
    //p是遍历指针  
    BiTree p = T;
    //p不为空或者栈不空时循环  
    while (p || !stack.empty())
       {
        if (p != NULL)
                  {
            //存入栈中  
            stack.push(p);
            //遍历左子树  
            p = p->lchild;
         }
        else
                {
            //退栈
            p = stack.top();
            operation1(p->data); //对树中的结点进行操作
            stack.pop();
            //访问右子树  
            p = p->rchild;
        }
    } 
}
//后序遍历(非递归)
typedef struct BiTNodePost{
    BiTree biTree;
    char tag;
}BiTNodePost, *BiTreePost;

void PostOrder(BiTree T)
{
    stack<BiTreePost> stack;
    //p是遍历指针  
    BiTree p = T;
    BiTreePost BT;
    //栈不空或者p不空时循环  
    while (p != NULL || !stack.empty())
        {
        //遍历左子树  
        while (p != NULL)
               {
            BT = (BiTreePost)malloc(sizeof(BiTNodePost));
            BT->biTree = p;
            //访问过左子树  
            BT->tag = 'L';
            stack.push(BT);
            p = p->lchild;
        }
        //左右子树访问完毕访问根节点  
        while (!stack.empty() && (stack.top())->tag == 'R')
                {
            BT = stack.top();
            //退栈  
            stack.pop();
            BT->biTree;
            cout<<BT->biTree->data<<" ";
        }
        //遍历右子树  
        if (!stack.empty())
                {
            BT = stack.top();
            //访问过右子树  
            BT->tag = 'R';
            p = BT->biTree;
            p = p->rchild;
        }
    }
}
//层次遍历(非递归)
void LevelOrder(BiTree T)
{
    BiTree p = T; 
    queue<BiTree> queue;
    //根节点入队  
    queue.push(p);
    //队列不空循环  
    while (!queue.empty())
       {
        //对头元素出队  
        p = queue.front();
        //访问p指向的结点  
        operation1(p->data);
        //退出队列  
        queue.pop();
        //左孩子不为空,将左孩子入队  
        if (p->lchild != NULL)
                {
            queue.push(p->lchild);
        }
        //右孩子不空,将右孩子入队  
        if (p->rchild != NULL)
                {
            queue.push(p->rchild);
        }
    }
}
int main()
{
    int level = 1; //表层数
    BiTree T = NULL;
    cout << "请以先序遍历的方式输入扩展二叉树:"; //类似输入AB#D##C##
    CreateBiTree(&T);// 建立二叉树,没有树,怎么遍历
    cout << "先序遍历输出为:" << endl;
    PreOrderTraverse(T, level);//进行前序遍历,其中operation1()和operation2()函数表示对遍历的结点数据进行的处理操作
    cout << endl;
    cout << "中序遍历输出为:" << endl;
    InOrderTraverse(T, level);
    cout << endl;
    cout << "后序遍历输出为:" << endl;
    PostOrderTraverse(T, level);
    cout << endl;
        cout<<"非递归前序遍历输出为:"<<endl;
        PreOrder(T);
        cout<<endl;
        cout<<"非递归前序遍历输出为:"<<endl;
        InOrder(T);
        cout<<endl;
        cout<<"非递归前序遍历输出为:"<<endl;
        PostOrder(T);
        cout<<endl;
        cout<<"层序遍历输出为:"<<endl;
        LevelOrder(T);
        cout<<endl;
    return 0;
}

实验内容二:以实验内容一所示链表为存储结构,编写程序实现求二叉树节点个数、叶子节点个数、二叉树的高度以及交换二叉树所有子树的操作

在原先代码的基础上分别加入了三个计数和交换子树的代码

count.cpp

#include<iostream>
#include<stdlib.h>
#include<stack>
#include<queue>
using namespace std;

typedef char ElemType;

typedef  struct BiTNode
{
    ElemType data;//数据域
    struct BiTNode *lchild, *rchild;//左孩子和右孩子
}BiTNode, *BiTree;

//根据试验要求,用先序创建的方式进行构建二叉树
//邹老师代课时建议我们加上层次遍历,希望李老师喜欢哈哈
void CreateBiTree(BiTree *T)
{
    ElemType ch;
    cin >> ch;
    if (ch == '#')
        *T = NULL;  
    else
    {
        *T = (BiTree)malloc(sizeof(BiTNode));
        (*T)->data = ch;//生成结点
        CreateBiTree(&(*T)->lchild);//构造左子树
        CreateBiTree(&(*T)->rchild);//构造右子树    
    }
}

//先序遍历
void operation1(ElemType ch)
{
    cout << ch << " ";
}
//此处在输出的基础上,并输出层数
void operation2(ElemType ch, int level)
{
       cout << ch << "在第" << level << "" << "  ";
}


//先序遍历二叉树(递归方式)
void PreOrderTraverse(BiTree T, int level)
{
    if (T == NULL)
        return;
    operation1(T->data);
    //operation2(T->data, level); //可输出层数

    PreOrderTraverse(T->lchild, level + 1);
    PreOrderTraverse(T->rchild, level + 1);
}

//中序遍历二叉树(递归方式)

void InOrderTraverse(BiTree T,int level)
{
if(T==NULL)
return;
InOrderTraverse(T->lchild,level+1);

operation1(T->data);
//operation2(T->data, level); //可输出层数

InOrderTraverse(T->rchild,level+1);
}

//后序遍历二叉树(递归方式)

void PostOrderTraverse(BiTree T,int level)
{
if(T==NULL)
return;
PostOrderTraverse(T->lchild,level+1);
PostOrderTraverse(T->rchild,level+1);

operation1(T->data);
//operation2(T->data, level); //可输出层数
}


/* 思路:将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,再先序遍历T的右子树。*/
void PreOrder(BiTree T){
    stack<BiTree> stack;
    //p是遍历指针  
    BiTree p = T;
    //p不为空或者栈不空时循环
    while (p || !stack.empty())
{
        if (p != NULL)
                 {
            //存入栈中  
            stack.push(p);
            //对树中的结点进行操作
            operation1(p->data);
             //遍历左子树  
            p = p->lchild;
         }
        else
                {
                 //退栈  
            p = stack.top();
            stack.pop();
            //访问右子树  
            p = p->rchild;
        }
    } 
}
//中序遍历(非递归)
void InOrder(BiTree T)
{
    stack<BiTree> stack;
    //p是遍历指针  
    BiTree p = T;
    //p不为空或者栈不空时循环  
    while (p || !stack.empty())
       {
        if (p != NULL)
                  {
            //存入栈中  
            stack.push(p);
            //遍历左子树  
            p = p->lchild;
         }
        else
                {
            //退栈
            p = stack.top();
            operation1(p->data); //对树中的结点进行操作
            stack.pop();
            //访问右子树  
            p = p->rchild;
        }
    } 
}
//后序遍历(非递归)
typedef struct BiTNodePost{
    BiTree biTree;
    char tag;
}BiTNodePost, *BiTreePost;

void PostOrder(BiTree T)
{
    stack<BiTreePost> stack;
    //p是遍历指针  
    BiTree p = T;
    BiTreePost BT;
    //栈不空或者p不空时循环  
    while (p != NULL || !stack.empty())
        {
        //遍历左子树  
        while (p != NULL)
               {
            BT = (BiTreePost)malloc(sizeof(BiTNodePost));
            BT->biTree = p;
            //访问过左子树  
            BT->tag = 'L';
            stack.push(BT);
            p = p->lchild;
        }
        //左右子树访问完毕访问根节点  
        while (!stack.empty() && (stack.top())->tag == 'R')
                {
            BT = stack.top();
            //退栈  
            stack.pop();
            BT->biTree;
            cout<<BT->biTree->data<<" ";
        }
        //遍历右子树  
        if (!stack.empty())
                {
            BT = stack.top();
            //访问过右子树  
            BT->tag = 'R';
            p = BT->biTree;
            p = p->rchild;
        }
    }
}
//层次遍历(非递归)
void LevelOrder(BiTree T)
{
    BiTree p = T; 
    queue<BiTree> queue;
    //根节点入队  
    queue.push(p);
    //队列不空循环  
    while (!queue.empty())
       {
        //对头元素出队  
        p = queue.front();
        //访问p指向的结点  
        operation1(p->data);
        //退出队列  
        queue.pop();
        //左孩子不为空,将左孩子入队  
        if (p->lchild != NULL)
                {
            queue.push(p->lchild);
        }
        //右孩子不空,将右孩子入队  
        if (p->rchild != NULL)
                {
            queue.push(p->rchild);
        }
    }
}
//求二叉树的高度,遍历左右子树,分别计数,取最大值+1
int TreeHeight(BiTree T)
{
    int Height=0;
    if(T)
    {
        int LeftHeight=TreeHeight(T->lchild);
        int RightHeight=TreeHeight(T->rchild);
        Height=LeftHeight>=RightHeight?LeftHeight+1:RightHeight+1;
    }
    return Height;
}
//求结点个数,遍历中计数左右子树的根节点+1
int countNode(BiTree T)
{

    int nodenum=0;
    int l=0;
    int r=0;
    if(!T)
        return 0;
    if(T){
    l=countNode(T->lchild);
    r=countNode(T->rchild);
    }
    nodenum=l+r+1;
    return nodenum;
}
//求叶子结点个数,方法:在遍历中既没有左子树又没有左子树就是叶子叶子结点
int Leafcount(BiTree T,int &num)
{
    if(T)
    {
        if(T->lchild ==NULL &&T->rchild==NULL)
            num++;
        Leafcount(T->lchild,num);
        Leafcount(T->rchild,num);
    }
    return num;
}
//交换左右子树
void ReverseLeftRightChild(BiTNode **T)
{
    // 如果是叶子节点,则递归结束
    if (*T == NULL)
    {
        return;
    }

    swap((*T)->lchild, (*T)->rchild); // 直接使用swap交换函数比较方便,直接交换指针;
    ReverseLeftRightChild(&((*T)->lchild));
    ReverseLeftRightChild(&((*T)->rchild));
}

int main()
{
    int num=0;
    int level = 1; //表层数
    BiTree T = NULL;
    cout << "请以先序遍历的方式输入扩展二叉树:"; 
    CreateBiTree(&T);// 建立二叉树
    cout << "先序遍历输出为:" << endl;
    PreOrderTraverse(T, level);
    cout << endl;
    cout << "中序遍历输出为:" << endl;
    InOrderTraverse(T, level);
    cout << endl;
    cout << "后序遍历输出为:" << endl;
    PostOrderTraverse(T, level);
    cout << endl;
        cout<<"非递归前序遍历输出为:"<<endl;
        PreOrder(T);
        cout<<endl;
        cout<<"非递归前序遍历输出为:"<<endl;
        InOrder(T);
        cout<<endl;
        cout<<"非递归前序遍历输出为:"<<endl;
        PostOrder(T);
        cout<<endl;
        cout<<"层序遍历输出为:"<<endl;
        LevelOrder(T);
        cout<<endl;
    int height=TreeHeight(T);
    cout<<"二叉树的高度为:"<<height<<endl;
    int nodenum=countNode(T);
    cout<<"二叉树的结点个数为"<<nodenum<<endl;
    Leafcount(T,num);
    cout<<"二叉树的叶子结点个数为"<<num<<endl;
    ReverseLeftRightChild(&T);
    cout << "先序遍历输出为:" << endl;
    PreOrderTraverse(T, level);
    cout<<endl;
    return 0;
}

实验内容三:编写程序,实现哈夫曼树的创建、哈夫曼编码以及编码的实现

哈夫曼算法:

给定N个权值,{w1,w2,w3,···,wn}

  1. 用给定的权值构建N个左右子树都为空的二叉树,N棵形成森林;
  2. 从中选取权值最小和次最小的两棵树作为左右子树,构成新的二叉树,新二叉树的权值为两棵二叉树权值之和。我们约定左子树的权值要小于右子树的权值;
  3. 从原权值集合中删除在上一步中使用的两棵子树,并将新子树加入集合;
  4. 循环执行,最后可得一棵哈夫曼树。

代码如下:

构造哈夫曼树.c

/*构造哈夫曼树的算法*/
/*哈夫曼树,又称最优树,是一类带权路径长度最短的树。
树的带权路径长度,是树中所有叶子 节点的带权路径长度之和。
通常记做WPL=W1*L1+W2*L2+...+Wn*Ln。*/
#include<stdio.h>  
#include<stdlib.h>  
typedef int ElemType;  
struct BTreeNode  
{  
    ElemType data;  
    struct BTreeNode* left;  
    struct BTreeNode* right;  
};  
  
//1、输出二叉树,可在前序遍历的基础上修改。采用广义表格式,元素类型为int  
void PrintBTree_int(struct BTreeNode* BT)  
{  
    if (BT != NULL)  
    {  
        printf("%d", BT->data); //输出根结点的值  
        if (BT->left != NULL || BT->right != NULL)  
        {  
            printf("(");  
            PrintBTree_int(BT->left); //输出左子树  
            if (BT->right != NULL)  
                printf(",");  
            PrintBTree_int(BT->right); //输出右子树  
            printf(")");  
        }  
    }  
}  
  
//2、根据数组 a 中 n 个权值建立一棵哈夫曼树,返回树根指针  
struct BTreeNode* CreateHuffman(ElemType a[], int n)  
{  
    int i, j;  
    struct BTreeNode **b, *q;  
    b = malloc(n*sizeof(struct BTreeNode));  
    for (i = 0; i < n; i++) //初始化b指针数组,使每个指针元素指向a数组中对应的元素结点  
    {  
        b[i] = malloc(sizeof(struct BTreeNode));  
        b[i]->data = a[i];  
        b[i]->left = b[i]->right = NULL;  
    }  
    for (i = 1; i < n; i++)//进行 n-1 次循环建立哈夫曼树  
    {  
        //k1表示森林中具有最小权值的树根结点的下标,k2为次最小的下标  
        int k1 = -1, k2;  
        for (j = 0; j < n; j++)//让k1初始指向森林中第一棵树,k2指向第二棵  
        {  
            if (b[j] != NULL && k1 == -1)  
            {  
                k1 = j;  
                continue;  
            }  
            if (b[j] != NULL)  
            {  
                k2 = j;  
                break;  
            }  
        }  
        for (j = k2; j < n; j++)//从当前森林中求出最小权值树和次最小  
        {  
            if (b[j] != NULL)  
            {  
                if (b[j]->data < b[k1]->data)  
                {  
                    k2 = k1;  
                    k1 = j;  
                }  
                else if (b[j]->data < b[k2]->data)  
                    k2 = j;  
            }  
        }  
        //由最小权值树和次最小权值树建立一棵新树,q指向树根结点  
        q = malloc(sizeof(struct BTreeNode));  
        q->data = b[k1]->data + b[k2]->data;  
        q->left = b[k1];  
        q->right = b[k2];  
  
        b[k1] = q;//将指向新树的指针赋给b指针数组中k1位置  
        b[k2] = NULL;//k2位置为空  
    }  
    free(b); //删除动态建立的数组b  
    return q; //返回整个哈夫曼树的树根指针  
}  
  
//3、求哈夫曼树的带权路径长度  
ElemType WeightPathLength(struct BTreeNode* FBT, int len)//len初始为0  
{  
    if (FBT == NULL) //空树返回0  
        return 0;  
    else  
    {  
        if (FBT->left == NULL && FBT->right == NULL)//访问到叶子结点  
            return FBT->data * len;  
        else //访问到非叶子结点,进行递归调用,返回左右子树的带权路径长度之和,len递增  
            return WeightPathLength(FBT->left,len+1)+WeightPathLength(FBT->right,len+1);  
    }  
}  
  
//4、哈夫曼编码(可以根据哈夫曼树带权路径长度的算法基础上进行修改)  
void HuffManCoding(struct BTreeNode* FBT, int len)//len初始值为0  
{  
    static int a[10];//定义静态数组a,保存每个叶子的编码,数组长度至少是树深度减一  
    if (FBT != NULL)//访问到叶子结点时输出其保存在数组a中的0和1序列编码  
    {  
        if (FBT->left == NULL && FBT->right == NULL)  
        {  
            int i;  
            printf("结点权值为%d的编码:", FBT->data);  
            for (i = 0; i < len; i++)  
                printf("%d", a[i]);  
            printf("\n");  
        }  
        else//访问到非叶子结点时分别向左右子树递归调用,并把分支上的0、1编码保存到数组a  
        {   //的对应元素中,向下深入一层时len值增1  
            a[len] = 0;  
            HuffManCoding(FBT->left, len + 1);  
            a[len] = 1;  
            HuffManCoding(FBT->right, len + 1);  
        }  
    }  
}  
  
//主函数  
void main()  
{  
    int n, i;  
    ElemType* a;  
    struct BTreeNode* fbt;  
    printf("从键盘输入待构造的哈夫曼树中带权叶子结点数n:");  
    while(1)  
    {  
        scanf("%d", &n);  
        if (n > 1)  
            break;  
        else  
            printf("重输n值:");  
    }  
    a = malloc(n*sizeof(ElemType));  
    printf("从键盘输入%d个整数作为权值:", n);  
    for (i = 0; i < n; i++)  
        scanf(" %d", &a[i]);  
    fbt = CreateHuffman(a, n);  
    printf("广义表形式的哈夫曼树:");  
    PrintBTree_int(fbt);  
    printf("\n");  
    printf("哈夫曼树的带权路径长度:");  
    printf("%d\n", WeightPathLength(fbt, 0));  
    printf("树中每个叶子结点的哈夫曼编码:\n");  
    HuffManCoding(fbt, 0);  
}  

posted @ 2018-05-09 10:55  WittPeng  阅读(2811)  评论(0编辑  收藏  举报