树和二叉树

树和二叉树

【学习重点】

树的遍历

二叉树的性质

二叉树和数的存储表示

二叉树的遍历操作及算法实现

树与二叉树之间的转换

【学习难点】

二叉树遍历算法的非递归实现

基于二叉树的遍历实现二叉树的其他操作

线索二叉树

有关树的算法

一、          树的逻辑结构

1.      树的定义

在树中常常将数据元素称为结点。

树是n个结点的有限集合,当n=0时,称为空树;任意一颗非空树满足一下条件:

(1)    有且仅有一个特定的称为根的结点;

(2)    n>1时,除根结点之外的其余结点被分成m个互不相交的有限集合T1T2……Tm其中每个集合又是一棵树,并称为这个根结点的子树。

2.      树的基本术语

1)某结点所拥有的子树个数称为该结点的度;树中各结点的最大值称为该树的度。

2)度为0的结点称为叶子结点,也称终端结点;度不为0的点称为分支结点,也称为非终端结点。

3)某结点的子树的根节点称为该结点的孩子结点;反之,该结点称为其孩子结点的双亲结点;具有同一个双亲结点的孩子结点互称为兄弟结点。

4)如果树的结点序列n1,n2…..nk满足:结点ni是结点ni+1的双亲,则把n1,n2…..nk称为一条由n1, nk的路径;路径上经过的边数称为路径的长度。

5)如果从结点x到结点y有一条路径,那么x就称为y的祖先,y称为x的子孙。

6)规定根结点的层数为1,对其余任何结点,若某结点在第k层,则其孩子结点在第k+1层;树中所有结点的最大层数称为树的深度也称为树的高度。

7)将树中结点按从上层到下层、同层从左到右的顺序依次给它们编以从1开始的连续自然数,树的这种编号方式称为层序编号。

8)如果一颗树中结点的各子树从左到右是有次序的,即交换了结点各子树的相对位置,则构成不同的树,称这颗树为有序树;反之,称为无序数。

9m棵互不相交的树的结合构成森林。

3.树的抽象类型定义

ADT  Tree

Data                      

树是由一个根结点和若干颗子树构成,树中结点具有相同数据类型及层次关系。

Operation

Int Tree

前置条件:树不存在

输入:无

功能:初始化一颗树

输出:无

后置条件:构造一颗树

DestroyTree

前置条件:树已存在

输入:无

功能:销毁一棵树

输出:无

后置条件:释放该树占用的存储空间

PreOrder

前置条件:树已存在

输入:无

功能:前序遍历树

输出:树的前序遍历树

后置条件:树保持不变

PostOrder

前置条件:树已存在

输入:无

功能:后序遍历树

输出:树的后序遍历操作

后置条件:树保持不变

L everOrder

前置条件:树已存在

输入:无

功能:层序遍历树

后置条件:树保持不变

endADT

4、树的遍历操作

树中最基本的操作是遍历。树的遍历是从根结点出发,按照某种某种次序访问树中所有结点,使得每个结点被访问一次且仅被访问一次。遍历操作主要有前序遍历、后序遍历和层序遍历三种方式。

1)前序遍历

若树为空,则空操作返回;否则

访问根结点

按照从左到右的顺序前序遍历根结点的每一棵子树。

2)后序遍历

若树为空,则空操作返回;否则

按照从左到右的顺序后序遍历根结点的每一棵子树

访问根结点

3)层序遍历

树的层序遍历也称树的广度遍历,其操作定义为从树的第一层开始,自上而下逐层遍历,在同一层中,按从左向右的顺序对各个结点逐个访问。

二、树的存储结构

1.双亲表示法

由树的定义可知,树中的每一个结点都有且只有一个双亲结点。根据这一特性,可以用一维数组来存储树中的各个结点,数组中的一个元素对应树中的一个结点。数组元素包括树中结点的数据信息以及该结点的双亲在数组中的下标。树的这种存储方法称为双亲表示法。

data

parent

 

 

 

树的双亲表示法中数组元素的结构

其中data是数据域,存储树中的数据信息;

Parent是指针域,存储该结点的双亲在数组中的下标;

 

A

-1

B

0

C

0

D

1

E

1

F

1

G

2

H

2

I

4

            下标        data               parent

            0

            1

            2

            3

            4

            5

            6

            7

            8

           

                       树的双亲表示法

-1代表该结点无双亲结点,即该结点是双亲结点。

2 孩子表示法

1)多重链表表示法

链表中的每个结点包括一个数据域和多个指针域,每个指针域指向该结点的一个孩子结点。 

方案一:指针域的个数等于树的度

其中:data:数据域,存放该结点的数据信息;

      child1~childd:指针域,指向该结点的孩子。

方案二: 指针域的个数等于该结点的度

其中:data:数据域,存放该结点的数据信息;

      degree:度域,存放该结点的度;

      child1~childd:指针域,指向该结点的孩子结点。

2)孩子链表表示法

孩子链表的基本思想:把每个结点的孩子排列起来,看成是一个线性表,且以单链表存储,则n个结点共有 n 个孩子链表。这 n 个单链表共有 n 个头指针,这 n 个头指针又组成了一个线性表,为了便于进行查找采用顺序存储。最后,将存放 n 个头指针的数组和存放n个结点的数组结合起来,构成孩子链表的表头数组。  

struct CTNode          //孩子结点

{   

     int child;

     CTNode *next;

};

template <class DataType>

struct CBNode            //表头结点

{     

    DataType data;

    CTNode *firstchild;  

};

3 双亲孩子表示法

基本思想:将双亲表示法和孩子链表表示法相结合。仍将各结点的孩子结点分别组成单链表,同时用一维数组顺序存储树中的各结点,数组元素除了包括结点的数据信息和该链表的头指针外,还增设一个域存储该结点的双亲结点在数组中的下标。

4 孩子兄弟表示法

树的孩子兄弟表示法又称二叉链表表示法,方法为:链表中的每个结点除数据域外,还设置了两个指针分别指向该结点的第一个孩子和右兄弟。

data:数据域,存储该结点的数据信息;

firstchild:指针域,指向该结点第一个孩子;

rightsib:指针域,指向该结点的右兄弟结点。 

template   <class DataType>

struct TNode

{

     DataType data;

     TNode <DataType> *firstchild, *rightsib;

};

5 二叉树的逻辑结构

5. 1 二叉树的定义

 二叉树是n(n0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。

二叉树的特点:

 每个结点最多有两棵子树;

 二叉树是有序的,其次序不能任意颠倒。

注意:二叉树和树是两种树结构。

特殊的二叉树:

斜树:

1 .所有结点都只有左子树的二叉树称为左斜树;

2 .所有结点都只有右子树的二叉树称为右斜树;

3.左斜树和右斜树统称为斜树。

斜树的特点:

1. 在斜树中,每一层只有一个结点;

2.斜树的结点个数与其深度相同。

满二叉树:

在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上。

满二叉树的特点:

1.叶子只能出现在最下一层;

2.只有度为0和度为2的结点。

满二叉树在同样深度的二叉树中结点个数最多,

满二叉树在同样深度的二叉树中叶子结点个数最多。

完全二叉树:

对一棵具有n个结点的二叉树按层序编号,如果编号为i1in)的结点与同样深度的满二叉树中编号为i的结点在二叉树中的位置完全相同。在满二叉树中,从最后一个结点开始,连续去掉任意个结点,即是一棵完全二叉树。

完全二叉树的特点:

1. 叶子结点只能出现在最下两层且最下层的叶子结点都集中在二叉树的左面;

2. 完全二叉树中如果有度为1的结点,只可能有一个,且该结点只有左孩子。

3. 深度为k的完全二叉树在k-1层上一定是满二叉树。

4. 在同样结点个数的二叉树中,完全二叉树的深度最小。 

 

5.2 二叉树的基本性质

性质5-1 二叉树的第i层上最多有2i-1个结点(i1)。

证明:当i=1时,第1层只有一个根结点,而

             2i-1=20 =1,结论显然成立。

假定i=k1ki)时结论成立,即第k层上至多有2k-1个结点,  i=k+1时,因为第k+1层上的结点是第k层上结点的孩子,而二叉树中每个结点最多有2个孩子,故在第k+1层上最大结点个数为第k层上的最大结点个数的二倍,即2×2k-12k。结论成立。

性质5-2   一棵深度为k的二叉树中,最多有2k-1个结点,最少有k个结点。 

证明:由性质1,深度为k的二叉树中结点个数最多                                                     =2k-1;每一层至少要有一个结点,因此深度为k的二叉树,

至少有k个结点。

深度为k且具有2k-1个结点的二叉树一定是满二叉树,

深度为k且具有k个结点的二叉树不一定是斜树。

性质5-3   在一棵二叉树中,如果叶子结点数为n0,度为2的结点数为n2,则有: n0n21

证明n为二叉树的结点总数,n1为二叉树中度为1的结点数,则有:

         nn0n1n2 

在二叉树中,除了根结点外,其余结点都有唯一的一个分枝进入,一个度为1的结点射出一个分枝,一个度为2的结点射出两个分枝,所以有:

         nn12n21

因此可以得到:n0n2

 性质5-4  具有n个结点的完全二叉树的深度为 log2n 向下取整+1

性质5-5    对一棵具有n个结点的完全二叉树中从1开始按层序编号,则对于任意的序号为i1in)的结点(简称为结点i),有: 

1)如果i1,则结点i的双亲结点的序号为  i/2;如果i1,则结点i是根结点,无双亲结点。 

2)如果2in,则结点i的左孩子的序号为2i

如果2in,则结点i无左孩子。 

3)如果2i+1n,则结点i的右孩子的序号为2i+1;如果2i+1n,则结点 i无右孩子。 

5. 3 二叉树的抽象数据类型定义

ADT BiTree

Data

    由一个根结点和两棵互不相交的左右子树构成,

    结点具有相同数据类型及层次关系

Operation

 InitBiTree

      前置条件:无

      输入:无

      功能:初始化一棵二叉树 

      输出:无

      后置条件:构造一个空的二叉树

DestroyBiTree 

       前置条件:二叉树已存在

       输入:无

       功能:销毁一棵二叉树

       输出:无

       后置条件:释放二叉树占用的存储空间  

  PreOrder

      前置条件:二叉树已存在

      输入:无

      功能:前序遍历二叉树

      输出:二叉树中结点的一个线性排列

      后置条件:二叉树不变 

 InOrder  

      前置条件:二叉树已存在

      输入:无

      功能:中序遍历二叉树

      输出:二叉树中结点的一个线性排列

      后置条件:二叉树不变

 PostOrder

      前置条件:二叉树已存在

      输入:无

      功能:后序遍历二叉树

      输出:二叉树中结点的一个线性排列

      后置条件:二叉树不变    

  LeverOrder

      前置条件:二叉树已存在

      输入:无

      功能:层序遍历二叉树

      输出:二叉树中结点的一个线性排列

      后置条件:二叉树不变 

endADT

5. 4 二叉树的遍历操作

二叉树的遍历是指从根结点出发,按照某种次序访问二叉树中的所有结点,使得每个结点被访问一次且仅被访问一次。

前序(根)遍历

若二叉树为空,则空操作返回;否则:

①访问根结点;

②前序遍历根结点的左子树;

③前序遍历根结点的右子树。

中序(根)遍历

若二叉树为空,则空操作返回;否则:

①中序遍历根结点的左子树;

②访问根结点;

③中序遍历根结点的右子树。

后序(根)遍历

若二叉树为空,则空操作返回;否则:

①后序遍历根结点的左子树;

②后序遍历根结点的右子树。

③访问根结点;

层序遍历

二叉树的层次遍历是指从二叉树的第一层(即根结点)开始,从上至下逐层遍历,在同一层中,则按从左到右的顺序对结点逐个访问。

已知一棵二叉树的前序序列和中序序列,构造该二叉树的过程如下: 

1. 根据前序序列的第一个元素建立根结点;

2. 在中序序列中找到该元素,确定根结点的左右子树的中序序列;

3. 在前序序列中确定左右子树的前序序列;

4. 由左子树的前序序列和中序序列建立左子树;

5. 由右子树的前序序列和中序序列建立右子树。 

已知一棵二叉树的后序序列和中序序列,也可唯一确定一棵二叉树。

5.4 二叉树的存储结构及实现

5.4.1 顺序存储结构

二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置(下标)应能体现结点之间的逻辑关系——父子关系。 

深度为k的右斜树,k个结点需分配2k1个存储单元。

一棵二叉树改造后成完全二叉树形态,需增加很多空结点,造成存储空间的浪费。

二叉树的顺序存储结构一般仅存储完全二叉树。

5.4.2 二叉链表

基本思想:令二叉树的每个结点对应一个链表结点,链表结点除了存放与二叉树结点有关的数据信息外,还要设置指示左右孩子的指针。

其中,data:数据域,存放该结点的数据信息;

      lchild:左指针域,存放指向左孩子的指针;

      rchild:右指针域,存放指向右孩子的指针。

template <class DataType>

struct BiNode

{

    DataType data;

    BiNode<T> *lchild, *rchild;

};

具有n个结点的二叉链表中,有n+1个空指针。

template <class DataType>

class BiTree

{

public:

  BiTree( ){root = Creat(root);}             //构造函数,建立一棵二叉树

  ~BiTree( ){Release(root);}                    //析构函数

  void PreOrder( ){PreOrder(root);}     //前序遍历二叉树

  void InOrder( ){InOrder(root);}         //中序遍历二叉树

  void PostOrder( ){PostOrder(root);}  //后序遍历二叉树

  void LeverOrder( );                              //层序遍历二叉树

private:

  BiNode<DataType> *root;                    //指向根结点的头指针

  BiNode<DataType> *Creat(BiNode<DataType> *bt);    //构造函数调用

  void Release(BiNode<DataType> *bt);               //析构函数调用

   void PreOrder(BiNode<DataType> *bt);          //前序遍历函数调用

  void InOrder(BiNode<DataType> *bt);             //中序遍历函数调用

  void PostOrder(BiNode<DataType> *bt);         //后序遍历函数调用

};

前序遍历——递归算法

template <class DataType>

void BiTree<DataType> :: PreOrder(BiNode<DataType> *bt) 

{

    if (bt == NULL)  return;              //递归调用的结束条件

    else {

        cout << bt->data;                    //访问根结点bt的数据域

        PreOrder(bt->lchild);             //前序递归遍历bt的左子树

        PreOrder(bt->rchild);             //前序递归遍历bt的右子树  

    }

}

中序遍历——递归算法 

template <class DataType>

void BiTree<DataType> :: InOrder (BiNode<DataType> *bt)

{

     if (bt == NULL) return;                 //递归调用的结束条件

     else {

         InOrder(bt->lchild);                 //中序递归遍历bt的左子树

         cout << bt->data;                      //访问根结点bt的数据域

         InOrder(bt->rchild);                //中序递归遍历bt的右子树

    }

}

后序遍历——递归算法

template <class DataType>

void BiTree<DataType> :: PostOrder(BiNode<DataType> *bt)

     if (bt == NULL) return;                //递归调用的结束条件

     else {

         PostOrder(bt->lchild);              //后序递归遍历bt的左子树

         PostOrder(bt->rchild);             //后序递归遍历bt的右子树

         cout << bt->data;                      //访问根结点bt的数据域

    }

}

层序遍历

template <class DataType>

void BiTree<DataType> :: LeverOrder( )

{

     front = rear = -1;        //采用顺序队列,并假定不会发生上溢

     if (root == NULL) return;        //二叉树为空,算法结束

     Q[++rear] = root;                      //根指针入队

     while (front != rear)                  //当队列非空时

     {

          q = Q[++front];                    //出队

          cout << q->data;   

          if (q->lchild != NULL)  Q[++rear] = q->lchild;

          if (q->rchild != NULL)  Q[++rear] = q->rchild;

     }

}

构造函数——建立二叉树

template <class DataType>

BiNode<DataType> *BiTree<DataType>::Creat(BiNode<DataType> *bt)

{

     cin >> ch;                      //输入结点的数据信息,假设为字符

     if (ch == '# ') bt = NULL;                //建立一棵空树

     else {

        bt = new BiNode; bt->data = ch;  //生成一个结点,数据域为ch

        bt->lchild = Creat(bt->lchild);          //递归建立左子树

        bt->rchild = Creat(bt->rchild);          //递归建立右子树

    }

    return bt;

}

析构函数

Template<class D>

void BiTree<D>::Release (BiNode<D>*bt)

{

     if(bt!=NULL)

   {

       Release(bt->lchild);

       Release(bt->rchild);

       delete bt;

   }

}

设计算法求二叉树的结点个数。

void Count(BiNode *root)  //count为全局量并已初始化为0

{

    if (root == NULL) return;

    else {

         Count(root->lchild);

         count++;

         Count(root->rchild);

   }

}

设计算法按前序次序打印二叉树中的叶子结点。

void PreOrder(BiNode *root)

{

    if (root == NULL) return;

    else {

        if (!root->lchild && !root->rchild)

               cout<<root->data;

        PreOrder(root->lchild);

        PreOrder(root->rchild);

   }

}

设计算法求二叉树的深度。 

int  Depth(BiNode *root)

{

    if (root == NULL) return 0;

    else {

         hl= Depth(root->lchild);

         hr= Depth(root ->rchild);

         return max(hl, hr)+1;

    }

}

设计算法求树中结点 x 的第 i 个孩子。 

TNode *Search(TNode *root, DataType x, int i)

{

    if (root->data == x) {

        j=1;

        p=root->firstchild;

        while (p!=NULL && j<i)

        {

             j++; 

             p=p->rightsib;

        }

        if (p != NULL) return p;

        else return NULL;

    }

    Search(root->firstchild, x, i);

    Search(root->rightsib, x, i);

}

5.4.3 三叉链表

在二叉链表的基础上增加了一个指向双亲的指针域。

其中:

datalchildrchild三个域的含义同二叉链表的结点结构;

parent域为指向该结点的双亲结点的指针。

5. 4 线索链表

线索:将二叉链表中的空指针域指向前驱结点和后继结点的指针被称为线索;

线索化:使二叉链表中结点的空链域存放其前驱或后继信息的过程称为线索化;

线索链表:加上线索的二叉链表称为线索链表。

ltag 0:  lchild指向该结点的左孩子

      1: lchild指向该结点的前驱结点

rtag  rchild指向该结点的右孩子

     1: rchild指向该结点的后继结点

enum flag {Child, Thread}; 

template  <class DataType>

struct ThrNode

{

     DataType data;

     ThrNode<DataType>  *lchild, *rchild;

     flag ltag, rtag;

};

二叉树的遍历方式有4种,故有4种意义下的前驱和后继,相应的有4种线索二叉树:

 前序线索二叉树

 中序线索二叉树

 后序线索二叉树

 层序线索二叉树

中序线索链表类的声明:

template <class DataType>

class InThrBiTree

{

public:

    InThrBiTree( );         //构造函数,建立中序线索链表

    ~ InThrBiTree( );      //析构函数,释放各结点的存储空间

    ThrNode *Next(ThrNode<DataType> *p);  //查找p的后继

    void InOrder( );                            //中序遍历线索链表

private:

    ThrNode *root;                             //指向线索链表的头指针

    ThrNode<DataType> *Creat(ThrNode<DataType> *bt); 

    void ThrBiTree(ThrNode<DataType> *bt, 

                              ThrNode<DataType> *pre); //构造函数调用

};

构造函数Creat

ThrNode<D>*InThrBiTree<D>::Creat(ThrNode<D>*bt)

{

cin>>ch;

if(ch=='#') bt=NULL;

else 

{

bt=new ThrNode;

bt->data=ch;

bt->ltag=0;

bt->rtag=0;

bt->lchild=Creat(lchild);

bt->rchild=Creat(rchild);

}

return bt;

}

中序线索化链表算法ThrBiTree

template <class D>

void InThrBiTree<D> ::ThrBiTree(ThrNode<D> *bt, ThrNode<D> *pre)

{

     if (bt == NULL) return;

     ThrBiTree(bt->lchild, pre);

     if (bt->lchild == NULL) {              //bt的左指针进行处理

         bt->ltag = 1;   

         bt->lchild = pre;                    //设置pre的前驱线索

     }

     if (bt->rchild == NULL) bt->rtag = 1;    //bt的右指针进行处理

     if (pre->rtag == 1) pre->rchild = bt;      //设置pre的后继线索

     pre = bt;

     ThrBiTree(bt->rchild, pre);

}

中序线索链表构造函数算法InThrBiTree

template<class D>

InThrBiTree<D>::InThrBiTree

{

root=Creat(root);

pre=NULL;

ThrBiTree(root,pre);

}

中序线索链表查找后继

template <class DataType>

ThrNode<DataType> *InThrBiTree<DataType> :: Next(

                                                                     ThrNode<DataType> *p)

{

     if (p->rtag == 1) 

         q = p->rchild;                //右标志为1,可直接得到后继结点

     else {

         q = p->rchild;                     //工作指针q指向结点p的右孩子

         while (q->ltag == 0)           //查找最左下结点

          q = q->lchild;

     }

     return q;

}

中序线索链表的遍历算法InOrder

template<class D>

void InThrBiTree<D>::InOrder()

{

if(root==NULL) return;

p=root;

while(p->ltag==0)

p=p->lchild;

cout<<p->data;

while(p->rchild!=NULL)

{

p=Next(p);

cout<<p->data;

}

}

5.5 二叉树遍历的非递归算法

5.5.1 前序遍历非递归算法

template <class DataType>

void BiTree::PreOrder(BiNode<DataType> *root) 

{

     top = -1;      //采用顺序栈,并假定不会发生上溢

     while (root != NULL || top != -1)

     {

         while (root != NULL)

         {

             cout<<root->data;

             s[++top] = root;

             root = root->lchild;  

         }

         if (top != -1) { 

             root = s[top--];

             root = root->rchild;  

         }

     }

}

5.5.2中序遍历非递归算法

template <class DataType>

void BiTree::PreOrder(BiNode<DataType> *root) 

{

     top = -1;      //采用顺序栈,并假定不会发生上溢

     while (root != NULL || top != -1)

     {

         while (root != NULL)

         {             

             s[++top] = root;

             root = root->lchild;  

         }

         if (top != -1) { 

             root = s[top--]; 

             cout<<root->data;

             root = root->rchild;  

         }

     }

}

 5.5.3后序遍历非递归算法

栈元素类型定义如下:

template <class DataType>

struct element

{

  BiNode<DataType> *ptr;

  int flag;                   

};

后序遍历:

template <class DataType>

void BiTree<DataType> :: PostOrder(BiNode<DataType> *bt) 

{

    top = -1;                                   //采用顺序栈,并假定栈不会发生上溢

    while (bt != NULL || top != -1)          //两个条件都不成立才退出循环

    {

         while (bt != NULL)

         {

              top++; s[top].ptr = bt; s[top].flag = 1;  //root连同标志flag入栈

              bt = bt->lchild;  

          }

          while (top != -1 && s[top].flag == 2)  

          {

              bt = s[top--].ptr; cout << bt->data;

          }

          if (top != -1) {

              s[top].flag = 2; bt = s[top].ptr->rchild;

         }

    }

}

5.6 树、森林与二叉树的转换

1)树转换成二叉树:

     第一个孩子转换为左孩子;

     右兄弟转换为右孩子。

反之,则为二叉树转换为树。

2)森林转换为二叉树:

先将第1棵树转换为二叉树,第2棵树转换为第1棵树的右孩子,第3棵树转换为第2棵树的右孩子。

3)森林的遍历

森林有两种遍历方法:

⑴前序(根)遍历:前序遍历森林即为前序遍历森林中的每一棵树。 

⑵后序(根)遍历:后序遍历森林即为后序遍历森林中的每一棵树。  

 

5.7 哈夫曼树及哈夫曼编码

叶子结点的权值:对叶子结点赋予的一个有意义的数值量。

二叉树的带权路径长度:设二叉树具有n个带权值的叶子结点,从根结点到各个叶子结点的路径长度与相应叶子结点权值的乘积之和。

5.7.1 哈夫曼树

哈夫曼树:

给定一组具有确定权值的叶子结点,带权路径长度最小的二叉树。

哈夫曼树的特点:

1. 权值越大的叶子结点越靠近根结点,而权值越小的叶子结点越远离根结点。 

2. 只有度为0(叶子结点)和度为2(分支结点)的结点,不存在度为1的结点

哈夫曼算法基本思想:

 初始化:由给定的n个权值{w1w2,…,wn}构造n棵只有一个根结点的二叉树,从而得到一个二叉树集合F{T1T2,…,Tn}

 选取与合并:在F中选取根结点的权值最小的两棵二叉树分别作为左、右子树构造一棵新的二叉树,这棵新二叉树的根结点的权值为其左、右子树根结点的权值之和;

 删除与加入:在F中删除作为左、右子树的两棵二叉树,并将新建立的二叉树加入到F中;

  重复⑵、⑶两步,当集合F中只剩下一棵二叉树时,这棵二叉树便是哈夫曼树。 

存储结构:

struct element

{     int weight;

      int lchild, rchild, parent;

};

其中:weight:权值域,保存该结点的权值;

      lchild:指针域,结点的左孩子结点在数组中的下标;

      rchild:指针域,结点的右孩子结点在数组中的下标;

      parent:指针域,该结点的双亲结点在数组中的下标。

哈夫曼算法HuffmanTree:

void HuffmanTree(element huffTree[ ], int w[ ], int n ) {

    for (i = 0; i <2*n-1; i++) {

        huffTree [i].parent = -1;

        huffTree [i].lchild = -1;

        huffTree [i].rchild = -1;   

    }

    for (i = 0; i < n; i++) 

        huffTree[i].weight = w[i];

    for (k = n; k < 2*n-1; k++) {

        Select(huffTree, i1, i2); 

        huffTree[k].weight = huffTree[i1].weight+huffTree[i2].weight;

        huffTree[i1].parent = k; huffTree[i2].parent = k; 

        huffTree[k].lchild = i1; huffTree[k].rchild = i2;

    }

}

 

5.7.2 哈夫曼编码

编码:给每一个对象标记一个二进制位串来表示一组对象。例:ASCII,指令系统

等长编码:表示一组对象的二进制位串的长度相等。

不等长编码:表示一组对象的二进制位串的长度不相等。

前缀编码:一组编码中任一编码都不是其它任何一个编码的前缀 。前缀编码保证了在解码时不会有多种可能。

    

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值