第7章_树和二叉树

数据结构可分为线性结构、树型结构和图型结构三大类。前几章讨论的线性表、堆栈、队列、串和数组都是线性结构。树型结构中每个结点只允许有一个直接前驱结点,但允许有一个以上直接后继结点。树型结构有树和二叉树两种。二叉树是最多只允许有两个直接后继结点的有序树。树的操作实现比较复杂,但树可以转换为二叉树进行处理。

树的定义

树是由n(n>=0)个结点构成的集合。n=0的树称为空树;对n>0的树T,有:(1)有一个特殊的结点称为根结点,根结点没有前驱结点;(2)当n>1时,除根结点外其他结点被分成m(m>0)个互不相交的集合T1,T2,…,Tm,其中每一个集合Ti(1<=i<=m)本身又是一棵结构与树类同的子树。(显然树是递归定义的)
树结构表示了数据元素之间的层次关系。

树的常用术语有:
结点:结点由数据元素和构造数据元素之间关系的指针组成。
结点的度:结点所拥有子树的个数称为该结点的度。
叶结点:度为0的结点称为叶结点,叶结点也称作终端结点。
分支结点:度不为0的结点称为分支结点,分支结点也称作非终端结点。显然,一棵树中除叶结点外的所有结点都是分支结点。
孩子结点:树中一个结点的子树的根结点称作这个结点的孩子结点。孩子结点也称为后继结点。
双亲结点:若树中某结点有孩子结点,则这个结点就称作它的孩子结点的双亲结点。双亲结点又称作直接前驱结点。
兄弟结点:具有相同的双亲结点的结点称为兄弟结点。
树的度:树中所有结点的度的最大值称为该树的度。
结点的层次:从根结点到树中某结点所经路径上的分支数称为该结点的层次。根结点的层次规定为0,这样,其他结点的层次就是他的双亲结点的层次加1。
树的深度:树中所有结点的层次的最大值称为该树的深度。
无序树:如果一棵树中任意一个结点的各子树从左到右是无序的,即苦味交换了某结点各子树的相对位置,不构成不同的树,这样的树称为无序树。
有序树:如果一棵树中任意一个结点的各子树从左到右是有序的,即若交换了某结点各子树的相对位置则构成不同的树,这样的树称为有序树。
森林:m(m>=0)棵树的集合称为森林。自然界中树和森林的概念差别很大,但在数据结构中树和森林的概念差 别很小。从定义可知,一棵树由根结点和m棵子树组成,若把树中的根结点删除,则树就变成了包含m棵树的森林。当然,根据定义,一棵树也可以称作森林。

树的抽象数据类型

数据集合:树的结点集合,每个结点由数据元素和构造数据元素之间关系的指针组成。
操作集合:

二叉树

二叉树的定义

二叉树是n(n>=0)个有限结点构成的集合。n=0的树称为空二叉树;n>0的二叉树由一个根结点和两个互不相交的、分别称作左子树和右子树的子二叉树构成。
显然,二叉树是一种有序树。二叉树中某个结点即使只有一个子树也要区分是左子树还是右子树。
二叉树中所有结点的形态共有五种:空结点、无左右子树结点、只有左子树结点、只有右子树结点和左右子树均存在的结点。
满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子结点都在同一层上,这样的二叉树称作满二叉树。

完全二叉树:如果一棵具有n个结点的二叉树的结构与满二叉树的前n个结点的结构相同,这样的二叉树称作完全二叉树。
二叉树的抽象数据类型
数据集合:二叉树的结点集合,每个结点由数据元素和构造数据元素之间关系的指针组成。

二叉树的设计和实现

Bitree.h


typedef struct Node
{
    DataType data;              //数据域
    struct Node * leftChild;    //左子树域
    struct Node * rightChild;   //右子树域
} BiTreeNode;                   //结点的结构体定义

//初始化创建二叉树的头结点
void Initiate(BiTreeNode** root)
{
    *root = (BiTreeNode*)malloc(sizeof(BiTreeNode));
    (*root)->leftChild = NULL;
    (*root)->rightChild = NULL;
}

//若当前结点curr非空,在curr的左子树插入元素值为x的新结点
//原curr所指结点的左子树成为新插入结点的左子树
//若插入成功返回新插入结点的指针,否则返回空指针
BiTreeNode * InsertLeftNode(BiTreeNode*curr,DataType x)
{
    BiTreeNode*s,*t;
    if(curr == NULL) return NULL;

    t = curr->leftChild;        //保存原curr所指结点的左子树指针
    s = (BiTreeNode*)malloc(sizeof(BiTreeNode));
    s->data = x;
    s->leftChild = t;           //新插入结点的左子树为原curr的左子树
    s->rightChild = NULL;
    curr->leftChild = s;        //新结点成为curr的左子树
    return curr->leftChild;     //返回新插入结点的指针
}

//若当前结点curr非空,在curr的右子树插入元素值为x的新结点
//原curr所指结点的右子树成为新插入结点的右子树 
//若插入成功返回新插入结点的指针,否则返回空指针
BiTreeNode* InsertRightNode(BiTreeNode*curr,DataType x)
{
    BiTreeNode*s,*t;
    if(curr == NULL) return NULL;

    t = curr->rightChild;       //保存原curr所指结点的左子树指针
    s = (BiTreeNode*)malloc(sizeof(BiTreeNode));
    s->data = x;
    s->leftChild = NULL;            //新插入结点的左子树为原curr的左子树
    s->rightChild = t;
    curr->rightChild = s;           //新结点成为curr的左子树
    return curr->rightChild;        //返回新插入结点的指针
}

//释放空间
void Destroy(BiTreeNode**root)
{
    if((*root) != NULL && (*root)->leftChild != NULL)
        Destroy(&(*root)->leftChild);
    if((*root) != NULL && (*root)->rightChild != NULL)
        Destroy(&(*root)->rightChild);
    free(*root);
}


//若curr非空,删除curr所指结点的左子树
//若删除成功返回删除结点的双亲结点指针,否则返回空指针
BiTreeNode* DeleteLeftTree(BiTreeNode*curr)
{
    if(curr == NULL || curr->leftChild == NULL) return NULL;
    Destroy(&curr->leftChild);
    curr->leftChild = NULL;
    return curr;
}

//若curr非空,删除curr所指结点的右子树
//若删除成功返回删除结点的双亲结点指针,否则返回空指针
BiTreeNode* DeleteRightTree(BiTreeNode*curr)
{
    if(curr == NULL || curr->rightChild == NULL) return NULL;

    Destroy(&curr->rightChild);
    curr->rightChild = NULL;
    return curr;
}

main.c

#include <stdlib.h>
#include <stdio.h>

typedef char DataType;
#include "Bitree.h"

void main(void)
{
    BiTreeNode*root,*p,*pp;
    Initiate(&root);
    p = InsertLeftNode(root,'A');
    p = InsertLeftNode(p,'B');
    p = InsertLeftNode(p,'D');
    p = InsertRightNode(p,'G');
    p = InsertRightNode(root->leftChild,'C');
    pp = p;
    InsertLeftNode(p,'E');
    InsertRightNode(pp,'F');
}

二叉树的遍历

二叉树遍历

从二叉树的定义可知,一棵二叉树由三部分组成:根结点、左子树和右子树。苦味规定D,L,R分别代表“访问根结点”、“遍历根结点的左子树”、“遍历根结点的右子树”,则共有六种组合:LDR,DLR,LRD,RDL,DRL,RLD。由于先遍历左子树和先遍历右子树在算法设计上没有本质区别,所以这里只讨论六种组合的前三种:DLR,LDR,LRD。根据遍历算法对访问根结点处理的位置,称这三种遍历算法分别为前序遍历(DLR)、中序遍历(LDR)和后序遍历(LRD)。

二叉链存储结构下二叉树遍历的实现

void Visit(DataType item)
{
    printf("%c ",item);
}


//二叉树遍历函数
//前序遍历二叉树t,访问操作为Visit()函数
void PreOrder(BiTreeNode* t)
{
    if (t != NULL)
    {
        Visit(t->data);
        PreOrder(t->leftChild);
        PreOrder(t->rightChild);
    }
}

//中序遍历二叉树t,访问操作为Visit()函数
void InOrder(BiTreeNode*t)
{
    if(t != NULL)
    {
        InOrder(t->leftChild);
        Visit(t->data);
        InOrder(t->rightChild);
    }
}

//后序遍历二叉树t,访问操作为Visit()函数
void PostOrder(BiTreeNode*t)
{
    if(t != NULL)
    {
        PostOrder(t->leftChild);
        PostOrder(t->rightChild);
        Visit(t->data);
    }
}
#include <stdlib.h>
#include <stdio.h>

typedef char DataType;
#include "Bitree.h"

void main(void)
{
    BiTreeNode*root,*p,*pp;
    Initiate(&root);
    p = InsertLeftNode(root,'A');
    p = InsertLeftNode(p,'B');
    p = InsertLeftNode(p,'D');
    p = InsertRightNode(p,'G');
    p = InsertRightNode(root->leftChild,'C');
    pp = p;
    InsertLeftNode(p,'E');
    InsertRightNode(pp,'F');

    printf("先序遍历:");
    PreOrder(root->leftChild);
    printf("\n");

    printf("中序遍历:");
    InOrder(root->leftChild);
    printf("\n");

    printf("后序遍历:");
    PostOrder(root->leftChild);
    printf("\n");
}

这里写图片描述

二叉树应用

  1. 打印二叉树
    把二叉树逆时针旋转90度,按照二叉树的凹入表示法打印二叉树。显然,可把此算法设计成递归算法。由于把二叉树逆时针旋转90度后,在屏幕上方的首先是右子树,然后是根结点,最后是左子树,所以,打印二叉树算法是一种特殊的中序遍历算法。
//逆时针旋转90度打印二叉树bt,n为缩进层数,初始为0
void PrintBiTree(BiTreeNode*bt,int n)
{
    int i;
    if(bt == NULL) return ;             //递归出口

    PrintBiTree(bt->rightChild,n+1);    //遍历打印右子树
    //访问根结点
    for(i = 0;i < n; i++) printf(" ");
    if(n >= 0)
    {
        printf("---");
        printf("%c\n",bt->data);
    }
    PrintBiTree(bt->leftChild,n+1);     //遍历打印左子树
}
#include <stdlib.h>
#include <stdio.h>

typedef char DataType;
#include "Bitree.h"

void main(void)
{
    BiTreeNode*root,*p,*pp;
    Initiate(&root);
    p = InsertLeftNode(root,'A');
    p = InsertLeftNode(p,'B');
    p = InsertLeftNode(p,'D');
    p = InsertRightNode(p,'G');
    p = InsertRightNode(root->leftChild,'C');
    pp = p;
    InsertLeftNode(p,'E');
    InsertRightNode(pp,'F');

    printf("先序遍历:");
    PreOrder(root->leftChild);
    printf("\n");

    printf("中序遍历:");
    InOrder(root->leftChild);
    printf("\n");

    printf("后序遍历:");
    PostOrder(root->leftChild);
    printf("\n");

    PrintBiTree(root->leftChild,0);
}

这里写图片描述

  1. 查找数据元素
    在二叉树中查找数据元素操作的要求是:在bt为根结点指针的二叉树中查找数据元x,若查找到数据元素x时返回该结点的指针;若查找不到数据元素x时返回空指针。
    在二叉树bt中查找数据元素x的算法可设计成先序遍历算法,此时查找结点的次序是:首先在根结点查找,然后在左子树查找,最后在右子树查找。但是,与常规递归算法不同的是:此算法当在一个分支上的结点比较完后仍未查找到数据元素x时,要返回到该结点的双亲结点处继续查找。因此,此算法是一个回溯算法。
//查找
BiTreeNode * Search(BiTreeNode* bt,DataType x)
{
    BiTreeNode*p;

    if(bt == NULL) return NULL;         //空二叉树时的查找失败出口
    if(bt->data == x) return bt;        //查找成功出口

    if(bt ->leftChild != NULL)
    {
        p = Search(bt->leftChild,x);    //在左子树查找
        if(p != NULL) return p;         //查找成功时结束递归过程
    }

    if(bt->rightChild != NULL)
    {
        p = Search(bt->rightChild,x);   //在右子树查找
        if(p != NULL) return p;         //查找成功时结速递归过程
    }

    return NULL;                        //查找失败出口
}

main.c

#include <stdlib.h>
#include <stdio.h>

typedef char DataType;
#include "Bitree.h"

void main(void)
{
    BiTreeNode*root,*p,*pp;
    Initiate(&root);
    p = InsertLeftNode(root,'A');
    p = InsertLeftNode(p,'B');
    p = InsertLeftNode(p,'D');
    p = InsertRightNode(p,'G');
    p = InsertRightNode(root->leftChild,'C');
    pp = p;
    InsertLeftNode(p,'E');
    InsertRightNode(pp,'F');

    printf("先序遍历:");
    PreOrder(root->leftChild);
    printf("\n");

    printf("中序遍历:");
    InOrder(root->leftChild);
    printf("\n");

    printf("后序遍历:");
    PostOrder(root->leftChild);
    printf("\n");

    PrintBiTree(root->leftChild,0);
    p = Search(root->leftChild,'Z');
    if(p != NULL)
    {
        printf("找到了%c!\n",p->data);
    }
    else
    {
        printf("未找到!\n");
    }
}

这里写图片描述

线索二叉树

线索二叉树的概念

二叉树遍历算法提供了二叉树的一次性遍历,但二叉树遍历算法无法实现用户程序像分步遍历单链表那样分步遍历二叉树。线索二叉树就是专为实现分步遍历二叉树而设计的。线索二叉树可以实现像双向链表那样,即可以从前向后分步遍历二叉树,又可以从后向前分步遍历二叉树。

当某结点的左指针为空时,令该指针指向按某种方法遍历二叉树时得到的该结点的前驱结点;当某结点的右指针为空时,令该指针指向按某种方法遍历二叉树时得到的该结点的后继结点。仅仅这样做会使我们不能区分左指针指向的结点到底是左孩子结点还是前驱结点,右指针指向的结点到底是右孩子结点还是后继结点。因此,再在结点中增加两个线索标志位来区分这两种情况。线索标志位定义如下:

leftThread 为0,leftChild指向结点的左孩子结点,为1,leftChild指出结点的前驱结点。
rightThread为0,rightChild指向结点的右孩子结点,为1,rightChild指出结点的后继结点。

这里写图片描述

结点中指向前驱结点和后继结点的指针称为线索。

中序线索二叉树的设计

InThreadIterator.h


typedef struct Node
{
    DataType data;              //数据元素
    int leftThread;             //左线索
    struct Node* leftChild;     //左指针
    struct Node* rightChild;    //右指针
    int rightThread;            //右线索
} ThreadBiNode;

//中序线索化二叉树
//current为当前结点指针,pre为当前结点的中序前驱结点指针
void InThread(ThreadBiNode* current,ThreadBiNode** pre)
{
    if(current != NULL)
    {
        InThread(current->leftChild,pre);       //中序线索化左子树
        if(current->leftChild == NULL)
        {
            current->leftThread = 1;            //建立左线索标记
            current->leftChild = *pre;          //建立左线索指针
        }
        else current->leftThread = 0;

        if(current->rightChild != NULL)
            current->rightThread = 0;
        else
            current->rightThread = 1;

        if((*pre)->rightChild == NULL)
        {
            (*pre)->rightThread = 1;            //建立右线索标记
            (*pre)->rightChild = current;       //建立右线索指针
        }
        else
            current->rightThread = 0;

        *pre = current;                         //前序结指针等于当前结点指针
        InThread(current->rightChild,pre);      //中序线索化右子树
    }
}

//创建中序线索化二叉树
void CreatInThread(ThreadBiNode** root)
{
    ThreadBiNode* t = *root;        //保存原二叉树根结点指针
    ThreadBiNode* current,* pre = *root;

    //建立头结点
    *root = (ThreadBiNode*)malloc(sizeof(ThreadBiNode));
    if(t == NULL)
    {
        (*root)->leftThread = 0;
        (*root)->rightThread = 1;
        (*root)->leftChild = *root;
        (*root)->rightChild = *root;
    }
    else        //当二叉树非空
    {
        current = t;            
        (*root)->leftChild = t;     //置头结点的左指针
        (*root)->leftThread = 0;    //置头结点的左线索

        InThread(current,&pre);     //线索化二叉树

        pre->rightChild = *root;    //置最后一个结点的右指针
        pre->rightThread = 1;       //置最后一个结点的右线索
        (*root)->rightChild = pre;  //置头结点的右指针
        (*root)->rightThread = 1;   //置头结点右线索
    }
}

中序线索二叉树循环操作的设计

要循环操作中序线索二叉树的各结点,必须要有初始化中序线索二叉树函数、取中序线索二叉树第一个结点指针函数、取中序线索二叉树下一个结点指针函数和判断是否已到中序线索二叉树尾部函数。

//中序线索二叉树循环操作的设计
typedef struct 
{
    ThreadBiNode* root;         //头指针
    ThreadBiNode* current;      //当前结点指针
    int nextComplete;           //遍历结束标记
} ThreadBiTree;

//初始化中序线索二叉树
void ThreadInitiate(ThreadBiTree* tree,ThreadBiNode* root)
{
    tree->root = root;
    tree->current = root;
    if(root == NULL)
        tree->nextComplete = 1;
    else
        tree->nextComplete = 0;
}

//使中序线索 二叉树tree的当前结点指针指向中序遍历的第一个结点
void First(ThreadBiTree* tree)
{
    tree->current = tree->root;
    while(tree->current->leftThread == 0)
        tree->current = tree->current->leftChild;
    if(tree->current == tree->root) tree->nextComplete = 1;
    else tree->nextComplete = 0;
}

//使中序线索二叉树tree的当前结点 指针指向中序遍历的下一个结点
void Next(ThreadBiTree* tree)
{
    ThreadBiNode*p = tree->current->rightChild;
    if(tree->nextComplete == 1) return ;
    if(tree->current->rightThread == 0)
        while(p->leftThread == 0) p = p->leftChild;
    tree->current = p;
    if(tree->current == tree->root) tree->nextComplete = 1;
}

//如中序线索二叉树tree的nextComplete域值等于1,则表示已到尾部
int EndOfNext(ThreadBiTree* tree)
{
    return tree->nextComplete;
}

:编写一个程序,首先建立如图所示的不带头结点的二叉树,然后中序线索化该二叉树,最后用循环结构输出该中序线索化二叉树各结点的序列信息。

这里写图片描述

//中序线索二叉树循环操作的设计
typedef struct 
{
    ThreadBiNode* root;         //头指针
    ThreadBiNode* current;      //当前结点指针
    int nextComplete;           //遍历结束标记
} ThreadBiTree;

//初始化中序线索二叉树
void ThreadInitiate(ThreadBiTree* tree,ThreadBiNode* root)
{
    tree->root = root;
    tree->current = root;
    if(root == NULL)
        tree->nextComplete = 1;
    else
        tree->nextComplete = 0;
}

//使中序线索 二叉树tree的当前结点指针指向中序遍历的第一个结点
void First(ThreadBiTree* tree)
{
    tree->current = tree->root;
    while(tree->current->leftThread == 0)
        tree->current = tree->current->leftChild;
    if(tree->current == tree->root) tree->nextComplete = 1;
    else tree->nextComplete = 0;
}

//使中序线索二叉树tree的当前结点 指针指向中序遍历的下一个结点
void Next(ThreadBiTree* tree)
{
    ThreadBiNode*p = tree->current->rightChild;
    if(tree->nextComplete == 1) return ;
    if(tree->current->rightThread == 0)
        while(p->leftThread == 0) p = p->leftChild;
    tree->current = p;
    if(tree->current == tree->root) tree->nextComplete = 1;
}

//如中序线索二叉树tree的nextComplete域值等于1,则表示已到尾部
int EndOfNext(ThreadBiTree* tree)
{
    return tree->nextComplete;
}

main.c

#include <stdio.h>
#include <malloc.h>

typedef char DataType;
#include "InThreadIterator.h"

//创建二叉树结点函数
ThreadBiNode * GetTreeNode(DataType item,ThreadBiNode* left,ThreadBiNode* right)
{
    ThreadBiNode*p;
    p = (ThreadBiNode*)malloc(sizeof(ThreadBiNode));
    p->data = item;
    p->leftChild = left;
    p->rightChild = right;
    return p;
}

//创建如图所示的二叉树函数
void MakeCharTree(ThreadBiNode** root)
{
    ThreadBiNode*b,*c,*d,*e,*f,*g;
    g = GetTreeNode('G',NULL,NULL);
    d = GetTreeNode('D',NULL,g);
    b = GetTreeNode('B',d,NULL);
    e = GetTreeNode('E',NULL,NULL);
    f = GetTreeNode('F',NULL,NULL);
    c = GetTreeNode('C',e,f);
    *root = GetTreeNode('A',b,c);
}

void main(void)
{
    ThreadBiNode* root;
    ThreadBiTree tree;

    MakeCharTree(&root);

    CreatInThread(&root);

    printf("二叉树中序正向遍历序列为:");
    ThreadInitiate(&tree,root);     //循环初始化
    for(First(&tree);!EndOfNext(&tree);Next(&tree))
        printf("%c ",tree.current->data);
    printf("\n");
}

这里写图片描述

哈夫曼树

在数据通讯中,经常 需要将传送的文 字转换 为二进制字符 0和1组成的二进制吕,并称此过程为编码。例如,假设要传送的电文为ABACCDA,电文中只有A,B,C,D四种字符,若这四个字符采用表a所示的编码方案,则电文的代码为00010010101100代码总长度为14。若这四个字符采用b所示的编码方案,则电文 的代码为0110010101110,代码总长度为13。
哈夫曼树可用于构造代码总长度最短的编码方案。具体构造方法如下:设需要编码的字符集合为{d1,d2,…,dn},各个字符在电文中出现的次数集合为{w1,w2,…,wn},以d1,d2,…,dn作 为叶结点 ,以w1,w2,…,wn作为各叶结点 的权值 构造 一棵二叉树,规 定 哈 夫曼树中的左分去为0,右分支为1,则从根结点到每个叶结点所经过的分支对应的0和1组成的序列便为该结点对应字符的编码。这样的代码总长度最短的不等长编码称为哈夫曼编码。
这里写图片描述

设计和实现

Haffman.h


typedef struct 
{
    int weight;         //权值
    int flag;           //标记
    int parent;         //双亲结点下标
    int leftChild;      //左孩子下标
    int rightChild;     //右孩子下标
} HaffNode;             //哈夫曼树的结点结构

typedef struct 
{
    int bit[MaxN];      //数组
    int start;          //编码的起始下标
    int weight;         //字符的权值
} Code;                 //哈夫曼编码的结构


//建 立叶结点 个数为n权值 数组为weight的哈夫曼树haffTree
void Haffman(int weight[],int n,HaffNode haffTree[])
{
    int i,j,m1,m2,x1,x2;
    //哈夫曼树haffTree初始化。n个叶结的二叉树共有2n-1个结点
    for(i = 0;i < 2*n - 1; i++)
    {
        if(i < n) haffTree[i].weight = weight[i];
        else
            haffTree[i].weight = 0;
        haffTree[i].parent = -1;
        haffTree[i].flag = 0;
        haffTree[i].leftChild = -1;
        haffTree[i].rightChild = -1;
    }

    //构造哈夫曼树haffTree的n-1个非叶结点
    for(i = 0;i < n-1; i++)
    {
        m1 = m2 = MaxValue;
        x1 = x2 = 0;
        for(j = 0;j < n + i; j++)
        {
            if(haffTree[i].weight < m1 && haffTree[j].flag == 0)
            {
                m2 = m1;
                x2 = x1;
                m1 = haffTree[j].weight;
                x1 = j;
            }
            else if(haffTree[j].weight < m2 && haffTree[j].flag == 0)
            {
                m2 = haffTree[j].weight;
                x2 = j;
            }
        }

        //将找出的两棵权值 最小的子树合并为一棵子树
        haffTree[x1].parent = n + i;
        haffTree[x2].parent = n + i;
        haffTree[x1].flag = 1;
        haffTree[x2].flag = 1;
        haffTree[n + i].weight = haffTree[x1].weight + haffTree[x2].weight;
        haffTree[n + i].leftChild = x1;
        haffTree[n + i].rightChild = x2;
    }
}

//由n个结点的哈夫曼树 haffTree构造 哈夫曼编码haffCode
void HaffmanCode(HaffNode haffTree[],int n,Code haffCode[])
{
    Code* cd = (Code*)malloc(sizeof(Code));
    int i,j,child,parent;
    //求n个叶结点的哈夫曼编码
    for(i = 0;i < n; i++)
    {
        cd->start = n-1;                    //不等长编码的最后一位为n-1
        cd->weight = haffTree[i].weight;    //取得编码对应的权值
        child = i;
        parent = haffTree[child].parent;

        //由叶结点向上直到根结点
        while(parent != -1)
        {
            if(haffTree[parent].leftChild == child)
                cd->bit[cd->start] = 0;     //左孩子分支编码0
            else
                cd->bit[cd->start] = 1;     //右孩子分支编码1
            cd->start--;
            child = parent;
            parent = haffTree[child].parent;
        }

        for(j = cd->start + 1;j < n; j++)
            haffCode[i].bit[j] = cd->bit[j];            //保存每个叶结点的编码
        haffCode[i].start = cd->start + 1;              //保存叶结点编码的起始位
        haffCode[i].weight = cd->weight;                //保存编码对应的权值
    }
}

main.c

#include <stdio.h>
#include <stdlib.h>
#define MaxValue 10000      //设定的权值最大值
#define MaxBit 10           //设定的最大编码位数
#define MaxN 100            //设定的最大结点个数

#include "Haffman.h"

void main(void)
{
    int i,j,n = 4;
    int weight[] = {1,3,5,7};
    HaffNode * myHaffTree = (HaffNode*)malloc(sizeof(HaffNode)*(2*n+1));
    Code* myHaffCode = (Code*)malloc(sizeof(Code)*n);
    if(n > MaxN)
    {
        printf("给出的n越界,修改MaxN值!\n");
        exit(0);
    }
    Haffman(weight,n,myHaffTree);
    HaffmanCode(myHaffTree,n,myHaffCode);

    //输出每个叶结点的哈夫曼编码
    for(i = 0;i < n; i++)
    {
        printf("Weight = %d Code = ",myHaffCode[i].weight);
        for(j = myHaffCode[i].start;j < n; j++)
            printf("%d",myHaffCode[i].bit[j]);
        printf("\n");
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值