zoukankan      html  css  js  c++  java
  • 二叉树的基本操作及哈夫曼编码系统的实现

    实验环境: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("
    ");  
            }  
            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("
    ");  
        printf("哈夫曼树的带权路径长度:");  
        printf("%d
    ", WeightPathLength(fbt, 0));  
        printf("树中每个叶子结点的哈夫曼编码:
    ");  
        HuffManCoding(fbt, 0);  
    }  

  • 相关阅读:
    zookeeper学习笔记
    wsl笔记
    SSDB数据库笔记
    spring笔记
    redis笔记
    openresty配置
    openresty安装笔记
    mybatis笔记
    nginx配置
    STM32F373(青风)+CUBEMX快速上手
  • 原文地址:https://www.cnblogs.com/WittPeng/p/9012972.html
Copyright © 2011-2022 走看看