数据结构学习第五章树和二叉树

第五章树和二叉树

1树的定义

1.1定义

数据结构学习第五章树和二叉树_第1张图片

1.2基本术语

数据结构学习第五章树和二叉树_第2张图片

1.3二叉树定义

数据结构学习第五章树和二叉树_第3张图片

特点:

①每个结点最多有两个孩子(二叉树中不存在度大于2的结点)。

②子树有左右之分,次序不能颠倒。

③二叉树可以是空集合,跟可以有空的左子树或者空的右子树。

:二叉树不是树的特殊情况,二叉树的子树要区分左子树和右子树,而树无需区分。

例子:

具有三个节点的二叉树有五种不同形态。

数据结构学习第五章树和二叉树_第4张图片

树有两种形态

数据结构学习第五章树和二叉树_第5张图片

1.4二叉树的5种形态

数据结构学习第五章树和二叉树_第6张图片

2.树的应用案例

【案例1】数据压缩问题

将数据文件转换成0、1组成的二进制串,称之为编码。这些编码可以用哈夫曼树来实现。

如:

数据结构学习第五章树和二叉树_第7张图片

【案例2】利用二叉树求解表达式的值

数据结构学习第五章树和二叉树_第8张图片

3.二叉树

3.1二叉树性质

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

性质3:利用边数推理得来。

在这里插入图片描述

性质4表明了完全二叉树节点数n与完全二叉树深度k之间的关系

数据结构学习第五章树和二叉树_第9张图片

性质5:表明了完全二叉树中双亲结点编号与孩子结点编号之间的关系

3.2满二叉树

定义:一棵二叉树拥有最多结点为满二叉树。

数据结构学习第五章树和二叉树_第10张图片

3.3完全二叉树

定义:深度为k的具有n个结点的二叉树,当且仅当每一个结点都与深度为k的满二叉树中编号为1~n的结点对应时,成为完全二叉树。

注:根据定义满二叉树是完全二叉树。

例:

数据结构学习第五章树和二叉树_第11张图片

数据结构学习第五章树和二叉树_第12张图片

4二叉树存储结构

数据结构学习第五章树和二叉树_第13张图片

4.1二叉树的顺序存储

实现描述:按满二叉树的结点层次编号,依次存放二叉树中数据元素。

数据结构学习第五章树和二叉树_第14张图片

【类型定义】

//二叉树顺序存储表示
#define MAXTSIZE 100
Typedef TElemType SqBiTree[MAXSTIZE];
SqBiTree bt;

例(不是满二叉树时):

数据结构学习第五章树和二叉树_第15张图片

顺序存储缺点:

数据结构学习第五章树和二叉树_第16张图片

特点:结点间关系蕴含在存储位置,适于满二叉树和完全二叉树。

4.2二叉树的链式存储

二叉树链式存储的结点,包含了两个后继:一个左孩子和一个右孩子,还有一个前驱:一个双亲。

数据结构学习第五章树和二叉树_第17张图片

1.二叉链表

存储方式需要经常操作后继元素,可以定义一个结点包含:左孩子(lchild),右孩子(rchild)和数据元素本身(data)。

数据结构学习第五章树和二叉树_第18张图片

【定义实现】

typdef struct BiNode{
    TElemType data;
    struct BiNode *lchild,*rchild;//左右孩子指针
}BiNode,*BiTree;

二叉树的链式存储示意图

数据结构学习第五章树和二叉树_第19张图片

由于每个结点都有两个指针域(左孩子跟右孩子),所以当有n个结点时,必定有2n个链域。这里除了根节点,其余结点都会有一个相应的链连接到双亲结点,所以一共有n-1条链 ,那么空指针的个数为 2n-(n-1) 个。

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

数据结构学习第五章树和二叉树_第20张图片

2.三叉链表

存储方式需要经常用到前驱元素,可以定义一个结点包含:左孩子(lchild),右孩子(rchild)和数据元素本身(data)以及父亲结点(parent)。

数据结构学习第五章树和二叉树_第21张图片

【定义】

typedef struct TriTree{
    TElemType data;
    struct TriNode *lchild,*rchild,*parent;
}TriNode,*TriTree;

5.遍历二叉树

  • 遍历:顺着某一条搜索路径“ 访问 ”结点,使得每个结点均被访问一次,而且仅被访问一次。

注:“ 访问 ”定义很广泛,可以是对接点的各种处理(如:输出接点信息,修改接点数据值等),但要求这种访问不破坏原来的数据结构。

  • 遍历可以得到树结点的一个线性排列。
  • 遍历用途—它是树结构插入、删除、修改、查找和排序运算的前提,是二叉树一切运算的基础和核心。

5.1遍历方法

二叉树每个结点可归结为三个组成部分:根节点、左子树、右子树

数据结构学习第五章树和二叉树_第22张图片

设:L—左子树,D—根结点,R—右子树。

则遍历整个二叉树方案:DLR、LDR、LRD、DRL、RDL、RLD。

若规定先左后右,则只有三种情况:DLR—根左右(先序遍历)、LDR—左根右(中序遍历)、LRD—左右根(后序遍历)。

注:根据根的遍历优先,分为先序遍历、中序遍历、后续遍历。

【先序遍历例子】

数据结构学习第五章树和二叉树_第23张图片

【中序遍历】

数据结构学习第五章树和二叉树_第24张图片

【后序遍历】

数据结构学习第五章树和二叉树_第25张图片

【例题】

数据结构学习第五章树和二叉树_第26张图片

先序:A B D G C E H F

中序:D G B A E H C F

后序:G D B H E F C A

【例题2】用二叉树表示算术表达式

数据结构学习第五章树和二叉树_第27张图片

先序—前缀表示(波兰式): - + a × b - c d / e f

中序—中缀表示:a + b × c - d - e / f

后序—后缀表示(逆波兰式):a b c d - × + e f / -

5.2遍历确定二叉树

  • 若二叉树中各结点的值均不相同,则二叉树结点的先序序列、中序序列和后序序列是唯一的
  • 由二叉树的“ 先序序列+中序序列 ” ,或者由二叉树的“ 后续序列+中序序列 “可以唯一确定一颗二叉树。

【例】已知二叉树的先序和中序序列,构造出相应的二叉树

​ 先序:A B C D E F G H I J

​ 中序:C D B F E A I H G J

【解析】

由于先序是根左右序列,中序是左根右序列。所以由上束可以推出,A为根节点,C D B F E为A的左子树全部元素, I H G J 为A的右子树全部元素。

数据结构学习第五章树和二叉树_第28张图片

接着推出 A左子树 CDBFE,根据先序与中序序列定义,可以推出B位根节点,CD为B的左子树全部元素,FE为B的右子树全部元素。

数据结构学习第五章树和二叉树_第29张图片

依照这个方法分别对每个元素团细分为根节点、左子树右子树,分解到每个结点只有一个元素时,结果如下

数据结构学习第五章树和二叉树_第30张图片

5.3遍历算法实现(递归)

1.先序遍历

【算法解析】

数据结构学习第五章树和二叉树_第31张图片

【算法实现】

//先序
Status PreOrderTraverse(BiTree T){
    if(T==NULL) returnOK;//为空二叉树
    else{
        visit(T);//访问根节点,例如输出根节点:printf("%d\t",t->data);
        PreOrderTraverse(T->lchild); //递归遍历左子树
        PreOrderTraverse(T->rchild); //递归遍历右子树
    }
}

【算法例子】

void pre(BiTree T){
    if(T=NULL)
        return OK;
    else{
        printf("%d\t",T->data);  //打印
        pre(T->lchild); //递归遍历左子树
        pre(T->rchild); //递归遍历右子树
    }
}

2.中序遍历

相比于先序遍历,中序遍历仅需调换访问根的次序到左子树右子树中间。

【算法实现】

//中序
Status InOrderTraverse(BiTree T){
    if(T==NULL) return OK;//已是空树
    else{
        InOrderTraverse(T->lchild);//递归遍历左子树
        visit(T);//访问根结点
        InOrderTraverse(T->rchild);//递归遍历右子树
    }
}

3.后序遍历

相比于先序遍历,中序遍历仅需调换访问根的次序到左子树右子树最后。

【算法实现】

//后序
Status PostOrderTraverse(BiTree T){
     if(T==NULL) return OK;//已是空树
    else{
        InOrderTraverse(T->lchild);//递归遍历左子树
        InOrderTraverse(T->rchild);//递归遍历右子树
        visit(T);//访问根结点
    }
}

4.遍历分析

数据结构学习第五章树和二叉树_第32张图片

每个结点只访问一次。时间复杂度:O(n)。

在递归过程中,未访问结点需要临时存放到栈,所以栈占用最大辅助空间为空间复杂度。空间复杂度:O(n)。

5.4遍历算法(非递归)

1.中序遍历非递归算法

【算法思想】

①建立一个栈

②根结点进栈,遍历左子树。

③根结点出栈,输出根节点,遍历右子树。

【算法实现】

Status InOrderTraverse(BiTree T){
    BiTree p;
    InitStack(S);
    p=T;
    while(p || !StackEmpty(S)){
        if(p){
            Push(S,p);
            p=p->lchild;
        }else{
            Pop(S,q);
            printf("%c" ,q->data);
            p=q->rchild;
        }
    }//while
    return OK;
}

5.5二叉树的层次遍历

层次遍历:从根节点开始,从上到下每一个深度顺序访问,每个深度按照从左到右访问每个结点。每个结点只访问一次。

数据结构学习第五章树和二叉树_第33张图片

【算法思想】

使用一个队列:

①将根结点进队。

②队不空时循环:从队列中出列一个结点*p,访问它;

—若它有左孩子结点,将左孩子结点进队;

—若它有右孩子结点,将右孩子结点进队。

【算法实现】

//队列定义
typedef struct{
    BTNode data[MaxSize];  //存放队中元素
    int front,rear;  //队头和队尾
}SqQueue; //顺序循环队列类型

//二叉树层次遍历算法
void LevelOrder(BTNode *b){
    BTNode *p;
    SqQueue *qu;  //定义一个队列
    InitQueue(qu);//初始化队列
    enQueue(qu,b); //根节点入队
    while(!QueueEmpty(qu)){//若队列不为空
        deQueue(qu,p); //出队结点p
        printf("%c",p->data); //访问p结点
        if(p->lchild !=NULL)  //先判断左结点是否为空
            enQueue(qu,p->lchild);
        if(p->rchild !=NULL) //再判断有结点是否为空
            enQueue(qu,p->rchild); 
    }
}

5.6二叉树遍历的应用

1.二叉树建立

【例】数据结构学习第五章树和二叉树_第34张图片

这里用“ # "充当空结点字符。则对上图先序遍历得到字符串:ABC##DE#G##F###

【算法实现】

Status CreateBiTree(BiTree &T){
    scanf(&ch); //键盘输出字符
    if(ch == "#") T==NULL;
    else{
        T=new BiTNode;
        if(!T){//获取空间失败
            exit(OVERFLOW);
        }
        T->data = ch;  //生成当前根结点
        CreateBiTree(T->lchild);//构造左子树
        CreateBiTree(T->rchild);//构造右子树
    }
    return OK;
}

2.复制二叉树

【算法描述】

①如果是空树,递归结束;

②否则,申请新结点,复制根结点

—先递归复制左子树

—再递归复制右子树

int Copy(BiTree T, BiTree &NewT){
    if(T==NULL){
        NewT = NULL;
        return 0; //结束
    }else{
        NewT=new BiTNode;
        NewT->data = T->data;//复制值
        Copy(T->lchild,NewT->lchild);//复制左子树
        Copy(T->rchild,NewT->rchild);//复制右子树
    }
}

3.计算二叉树深度

【算法描述】

①如果是空树,则深度为0;

②否则,递归计算左子树深度记录为m,递归计算右子树的深度记为n,取n与m较大者+1。

【算法实现】

int Depth(BiTree T){
    if(T=NULL)
        return 0;//如果树为空返回0
    else{
        m=Depth(T->lchild);
        n=Depth(T->rchild);
        if(m>n) return(m+1);
        else return(n+1);
    }
}

4.计算二叉树结点总个数

【算法描述】

①如果是空树,结点个数为0。

②否则,结点个数=左子树的结点个数+右子树的节点个数+1。

【算法实现】

int NodeCount(BiTree T){
    if(T ==NULL)
        return 0;
    else{
        return NodeCount(T->lchild)+NodeCount(T->rchild)+1;
    }
}

5.计算二叉树叶子结点总数

【算法描述】

①如果是空树,则叶子结点为0。

②否则,为左子树叶子结点个数+右子树叶子结点个数。

int LeadCount(BiTree T){
    if(T==NULL)
        return 0;
    if(T->lchild == NULL & T->rchild == NULL)
        return 1;
    else
        return LeafCount(T->lchild)+LeafCount(T->rchild);
}

6线索二叉树

  • 为了方便直接查找某个结点在“ 某种遍历序列 ”中的前驱和后继结点。将空结点利用起来成为线索二叉树,这种利用起来空结点的指针成为“ 线索 ”。加上线索的二叉树成为线索二叉树(Threaded Binary Tree)。

空结点利用:

①如果某个结点左孩子为空,则将空的左孩子指针改为指向其前驱。

②如果某个结点右孩子为空,则将空的右孩子指针改为指向其后继。

【例】

数据结构学习第五章树和二叉树_第35张图片

为了区分lchild和rchild指针是指向孩子指针,还是前驱或后继指针,对二叉链表中增设两个标志域,ltag和rtag,且约定:

—ltag=0:lchild指向该节点左孩子;ltag=1:lchild指向该节点前驱。

—rtag=0:rchild指向该节点右孩子;rtag=1:rchild指向该节点后继。

节点结构如图:

在这里插入图片描述

【定义实现】

typedef struct BiThrNode{
    int data;
    int ltag,rtag;
    struct BiThrNode *lchild,*rchild;
}BiThrNode,*BiThrTree;

如先序线索二叉树:

数据结构学习第五章树和二叉树_第36张图片

【练习】

数据结构学习第五章树和二叉树_第37张图片

由于这里H的前驱与G的后继为空,这里增设了一个头结点:ltag=0,lchild指向根节点;rtag=1,rchild指向遍历序列最后一个结点。并且遍历中第一个结点的lchild和最后一个节点的rchild都指向头结点。

数据结构学习第五章树和二叉树_第38张图片

7.树和森林的存储结构

数据结构学习第五章树和二叉树_第39张图片

7.1双亲表示法

【定义】结构数组,存放树的结点,每个结点两个域:

—数据域:存放结点本身信息。

—双亲域:指示本结点的双亲结点在数组中的位置。

【例子】

一棵树数据结构学习第五章树和二叉树_第40张图片
数据存储:数据结构学习第五章树和二叉树_第41张图片

【特点】找双亲容易,找孩子难。

【实现】

//结点结构
typedef struct PTNode{
    TElemType data;  //数据域
    int parent;      //双亲域
}PTNode;

//树结构
#defind MAX_TREE_SIZE 100 
typedef struct{
    PTNode nodes[MAX_TREE_SIZE];
    int r,n;//记录根结点位置和结点个数
}PTree;

7.2孩子链表

把每个结点的孩子结点排列起来,看成一个线性表,用单链表存储。则n个结点有n个孩子链表。n个头指针又组成一个线性表,用顺序表存储。

【例】

数据结构学习第五章树和二叉树_第42张图片

【实现】

//孩子节点
typedef struct CTNode{
    int child;
    struct CTNode *next;
}*ChildPtr;

//双亲节点
typedef struct{
    TElemType data;
    ChildPtr firstchild;  //孩子链表头指针
}CTBox;

//树结构
typedef struct{
    CTBox nodes[MAX_TREE_SIZE];
    int n,r; //结点树与结点位置
}CTree;

【特点】找孩子容易,找双亲难。可以在双亲结点中再增加一个成员,指向这个结点的双亲。

【示意图】带双亲的孩子链表

数据结构学习第五章树和二叉树_第43张图片

7.3孩子兄弟(二叉链表表示法)

【定义】用二叉链表作树的存储结构,链表中每个结点的两个指针域分别指向第一个孩子结点和下一个兄弟结点

typedef struct CSNode{
    ElemType data;
    struct CSNode *firstchild,*nextsibling;
}CSNode,*CSTree;

【例子】

数据结构学习第五章树和二叉树_第44张图片

8.树和二叉树的转换

  • 将树转化成为二叉树进行处理,利用二叉树的算法来实现对树的操作。
  • 由于树和二叉树都可以用二叉链表(孩子兄弟表示法)作为存储结构,则以二叉链表作为媒介可以导出树和二叉树之间的对应关系。

数据结构学习第五章树和二叉树_第45张图片

8.1树转二叉树

①加线:在兄弟之间加一连线。

②抹线:对每个结点,除了其左孩子外,去除其余孩子之间的关系。

③旋转:以树根结点为轴心,整树顺时针转45°。

【例】

数据结构学习第五章树和二叉树_第46张图片

8.2二叉树转树

①加线:若p结点是某个双亲结点的左孩子,则将p结点的右子树的右子孙都与p的双亲结点链接起来。

②抹线:抹掉原二叉树中双亲域右孩子之间的连线。

③调整:将结点按层次排列,形成树结构。

【例】

数据结构学习第五章树和二叉树_第47张图片

在这里插入图片描述

8.3森林转二叉树

①将各棵树分别转换成二叉树

②将每棵树的根结点用线相连

③以第一棵树根结点为二叉树的根,再以根结点为轴心,顺时针旋转,构成二叉树型结构。

数据结构学习第五章树和二叉树_第48张图片

8.4二叉树转森林

①抹线:将二叉树中根结点与其右孩子连线,以及沿右分支搜索到的所有右孩子间连线全部抹掉,是之变成孤立的二叉树。

②还原:将孤立的二叉树还原成树。

数据结构学习第五章树和二叉树_第49张图片

9.树的遍历

  • 树的遍历:

—先根:若树不为空,则先访问根结点,然后依次先根遍历各棵子树。

—后根:若树不为空,则先依次后根遍历各棵子树,然后访问根结点。

—按层次遍历:若树不为空,则自上而下自左自右访问树中每个结点。

【例】

数据结构学习第五章树和二叉树_第50张图片

  • 森林的遍历:

将森林看成三个部分构成:第一棵树的根结点;第一棵树子树森林;其他树构成的森林。

—先序:先访问森林第一棵树的结点,先序遍历森林中第一棵树的子树森林,先序遍历其他树构成森林。

—中序:中序遍历森林中第一棵树的子树森林,再访问森林第一棵树的结点,中序遍历其他树构成森林。

【例子】

数据结构学习第五章树和二叉树_第51张图片

10.哈夫曼树

  • 有一个判别树来判别成绩的等级。

数据结构学习第五章树和二叉树_第52张图片

当每次输入量很大时,若学生成绩数据共10000个,比较总次数为10000×(1×5%+2×15%+3×40%+4×10%)=31500次。

若将判别树进行修改:

数据结构学习第五章树和二叉树_第53张图片

此时比较总次数为22000次。

综上,不同判别树效率是不一样的。而效率最高的判别树称为" 哈夫曼树 “,也称” 最优二叉树 "。

  • 路径:从树中一个结点到另一个结点之间的分支构成这两个结点间的路径。
  • 结点的路径长度:两结点间的路径上的分支树。

【例】

数据结构学习第五章树和二叉树_第54张图片

  • 树的路径长度:从树根到每一个结点的路径长度之和,记作:TL

数据结构学习第五章树和二叉树_第55张图片

二叉树中若结点数目相同,完全二叉树是路径长度最短的二叉树。

  • 权(weight):将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。
  • 结点的带权路径长度:从根结点到该结点之间的路径长度与该结点权的乘积。
  • 树的带权路径长度:树中所有叶子结点的带权路径长度之和。数据结构学习第五章树和二叉树_第56张图片

【例】有4个结点a,b,c,d,权值分别为7,5,2,4,构造以此4个结点为叶子结点的二叉树。

数据结构学习第五章树和二叉树_第57张图片

则带权路径长度:WPL=7×2+5×2+2×2+4×2=36。

数据结构学习第五章树和二叉树_第58张图片

则带权路径长度:WPL=4×2+7×3+5×3+2×1=46。

10.1哈夫曼树

最优树:即带权路径长度(WPL)最短的树。

注:“ 带权路径长度最短 ” 是在 “ 度相同 ”的树中比较而得出的,因此优最优二叉树、最优三叉树等。

哈夫曼树:最优二叉树,即带权路径长度(WPL)最短的二叉树。

【例】

数据结构学习第五章树和二叉树_第59张图片

满二叉树不一定是哈夫曼树。具有相同带权结点的哈夫曼树不唯一。

10.2构造哈夫曼树

【特点】哈夫曼树中权值越大的叶子离根越近。

贪心算法:构造哈夫曼树首先选择权值小的叶子结点。

【思路】

①根据给定的权值,将每个结点单独称为一个森林。

②选用最小的两个结点组成新树,删除这两棵树,同时二叉树加入森林中。

③重复②,直至森林中只有一棵树为止,这棵树称为哈夫曼树。

【例】有4个结点a ,b ,c , d,权值分别为7 ,5 , 2 ,4,构造哈夫曼树

数据结构学习第五章树和二叉树_第60张图片
数据结构学习第五章树和二叉树_第61张图片
数据结构学习第五章树和二叉树_第62张图片
数据结构学习第五章树和二叉树_第63张图片

哈夫曼树的结点只有度为0或2,没有度为1的结点。

包含n个叶子结点的哈夫曼树中共有2n-1个结,点。

【例】有5个结点a ,b ,c ,d ,e,权值分别为7,5,5,2,4,构造哈夫曼树

数据结构学习第五章树和二叉树_第64张图片
数据结构学习第五章树和二叉树_第65张图片
数据结构学习第五章树和二叉树_第66张图片
数据结构学习第五章树和二叉树_第67张图片
数据结构学习第五章树和二叉树_第68张图片

10.3哈夫曼树构造算法实现

1.结点类型定义

typedef struct{
    int weight;
    int parent,lch,rch;
}HTNode,*HuffmanTree;

2.构造算法

【示意图】

数据结构学习第五章树和二叉树_第69张图片

【实现】

void CreatHuffmanTree(HuffmanTree HT,int n){//构造哈夫曼树
    if(n<=1) return;
    m=2*n-1;  //一共2n-1个结点
    HT=new HTNode[m+1]; //由于0单元未用,所以需要+1。HT[m]表示根结点
    for(i=1;i<=m;++i){//初始化表
        HT[i].lch=0;
        HT[i].rch=0;
        HT[i].parrnt=0;
    }
    for(i=1;i<=n;++i)
        scanf("%c",&HT[i].weight);//输出前n个元素的权值
    //初始化结束,开始建立哈夫曼树
    for(i=n+1;i<=m;i++){
        Select(HT,i-1,s1,s2);//在HT中选择两个双亲域为0,且权值最小的结点,并返回他们在HT中的序号s1和s2。
        HT[s1].parent=i;//将两个最小结点结合,双亲结点指向当前i结点。即删除s1,s2组成新树
        HT[s2].parent=i;
        HT[i].lch=s1;//双亲结点的左孩子右孩子设置
        HT[i].rch=s2;
        HT[i].weight=HT[s1].weight+HT[s2].weight;//i的权值为左右孩子权值之和
    }
}

10.4哈夫曼编码

  • 在远程通讯中,要将待传字符转换成由二进制的字符串:

例如:设置等长编码A—00,B—01,C—10,D—11。

则:ABACCDA—00 01 00 10 10 11 00

倘若将编码设计长度不等的二进制编码,即让待传送字符串中出现次数较多的字符采用尽可能短的编码,则转换的二进制字符串便可能减少。

例如:设置不等长编码A—0,B—00,C—1,D—01。

则:ABACCDA—0 00 0 1 1 01 0 。这里会发生重码,导致从编码转换为字符时发生错误。

:所以在设计长度不等的编码时,必须使任一字符编码都不是另一个字符的编码的前缀。

  • 使用哈夫曼编码可以避免这个问题:

(1)统计字符集中每个字符在电文中出现的平均概率。

(2)利用哈夫曼树的特点:权越大的叶子离根越近,将每个字符的概率值作为权值,构成哈夫曼树。则概率越大的结点,路径越短,编码越短。

(3)把哈夫曼树的每个分支标上0或1:结点左分支标0,右分支标1。把从根到每个叶子的路径上的标号连接起来,作为该叶子代表的字符编码。

【例】要传输的字符集D={C , A , S , T , ; }其对应的出现频率w={2 , 4 , 2 , 3 , 3}

构造哈夫曼树并标左右分支后得

数据结构学习第五章树和二叉树_第70张图片

则T—00,; —01,A—10,C—110,S—111。这些为哈夫曼编码。

例如电文为CAS;CAT;SAT;AT,其编码为110 10 111 01 110 10 00 01 111 10 00 01 10 00

反之编码为1101000,得出CAT
s1].weight+HT[s2].weight;//i的权值为左右孩子权值之和
}
}


### 10.4哈夫曼编码

* 在远程通讯中,要将待传字符转换成由二进制的字符串:

例如:设置等长编码A—00,B—01,C—10,D—11。

则:ABACCDA—00 01 00 10 10 11 00

倘若将编码设计长度不等的二进制编码,即让待传送字符串中出现次数较多的字符采用尽可能短的编码,则转换的二进制字符串便可能减少。

例如:设置不等长编码A—0,B—00,C—1,D—01。

则:ABACCDA—0 00 0 1 1 01 0 。这里会发生重码,导致从编码转换为字符时发生错误。

**注**:所以在设计长度不等的编码时,必须使任一字符编码都不是另一个字符的编码的前缀。

* 使用哈夫曼编码可以避免这个问题:

(1)统计字符集中每个字符在电文中出现的平均概率。

(2)利用哈夫曼树的特点:权越大的叶子离根越近,将每个字符的概率值作为权值,构成哈夫曼树。则概率越大的结点,路径越短,编码越短。

(3)把哈夫曼树的每个分支标上0或1:结点左分支标0,右分支标1。把从根到每个叶子的路径上的标号连接起来,作为该叶子代表的字符编码。

【例】要传输的字符集D={C , A , S , T , ; }其对应的出现频率w={2 , 4 , 2 , 3 , 3}

构造哈夫曼树并标左右分支后得

image-20230721174230263

则T—00,; —01,A—10,C—110,S—111。这些为哈夫曼编码。

例如电文为CAS;CAT;SAT;AT,其编码为110 10 111 01 110 10 00 01 111 10 00 01 10 00

反之编码为1101000,得出CAT

学习视频:数据结构——王卓;
参考文献:数据机构C语言版第2班——严蔚敏

你可能感兴趣的:(咖啡ice的数据结构学习记录,数据结构,学习)