草庐IT

数据结构基础—树与二叉树(2)

T,a,o 2023-03-28 原文

五、线索二叉树

1.什么是线索二叉树

遍历二叉树的结果是,求得结点的一个线性序列,结点中再添加两个标记“LTag和RTag”,来判断当前结点是否有孩子

  • 若左子树不空,则,将lchild指向其左子树,且左标志域的值为“Link”;否则(空),lchild指向前驱,且左标志的值为“Thread”
  • 若右子树不空,则,将lchild指向其右子树,且右志域的值为“Link”;否则(空),lchild指向后继,且右标志的值为“Thread”

总之,不空正常指向。空,左:指向前驱;右:指向后继(若无头针,则可能有空悬)

类型描述如下

typedef struct BiNode {
	Datatype data;//数据内容
	struct BiNode  *Lchild;//指向左孩子结点
	struct BiNode  *rchild;//指向右孩子结点
	int Ltag;//值为0,则Lchild指向该结点的左孩子;值为1.指向该结点的前驱结点
	int rtag;//值为0,则rchild指向该结点的右孩子;值为1.指向该结点的后继结点
} BiNode ;

三种遍历

先序

中序

后序

要会自己划线,实线是指针,虚线是线索

2.建立(线索化)线索二叉树

才用递归的方法,以中序为例,先处理左子,再处理当前结点,最后处理右子即可

需要添加辅助指针pre:指向当前访问的指针p的前驱(逻辑前驱)

//不带头结点
void inThreading(p){
    if(p){//p就是根结点,不空
        inThreading(p->lchild);//处理左子
        //处理当前结点
        if(左子空){
            p->LTag = Thread;
            p->lchild = pre;//前驱
        }
        if(右子空){
            p->RTag =Thread;//标记为无右孩子
        }
        if(pre&&pre->RTag == Thread){pre不空且没有右孩子(pre是当前的前驱)
            pre->rchild = p;
        }
        pre = p;//我自己就是自己的前驱
        inThreading(p->rchild);//处理右子
    }
}

3.遍历线索二叉树

中序不需要栈,先序和后序需要栈

以中序为例,先找到第一个结点,然后判断其是否有右子树,若无,则访问其线索指向的地址(当前的后继),如果有右子树,则让p = p->rchild继续按中序遍历

//有头结点
while(树不空){
    while(左有子树){
        p = p->lchild;//一直沿着左链走,找到第一个没有左子的结点p
        访问一下p 
        while(p->rchild == Thread&& != T) p = p->rchild并访问p的后继结点
        否则(p有右子树):p = p->rchild;//成为新的根结点
    }
}

六、树和森林的表示方法

1.双亲表示法

typedef struct PTNode{
    Elem data;
    int parent;//双亲位置域,-1则为根
}PTNode;

2.孩子链表法

在双亲链表的基础上,增加一个指针域,来依次存放该结点的孩子结点(深度为一)

3.左孩子右兄弟表示法

  • firstchild:存放其左边的结点

  • nextsibling:同级左结点的全部兄弟结点

typedef struct CSNode{
    Elem data;
    struct CSNode *firstchild.*nextsibling;
}CSNode.*CSTree;

4.树、森岭与二叉树的转化

a.数和二叉树

树和转化为二叉树

  • 在树的每层从左至右在兄弟结点之间添加虚线
  • 除左第一个结点,父结点与所有的子结点的连线去掉
  • 将原来的实,线左移;将后添的虚线便实线,右移

变化之后的二叉树的根结点没有右子树,左结点还是原来的左结点,所有沿右链往下的结点均是该解结点的兄弟结点

二叉树还原回树

  • 将父结点与该左结点的右链间全部添加虚线
  • 将所有的右分支的连线全部去掉
  • 将虚线相连的结点上移

b.森林与二叉树

森林转化为二叉树

  • 先将森林中的每一棵树化为二叉树
  • 从最后一颗二叉树开始,每一颗 二叉树的根作为前一颗二叉树根的右子

二叉树还原为森林

  • 将二叉树的根右子依次去掉
  • 在每一颗二叉树还原回树

七、树和森岭的遍历

1.树的遍历

总体分为先根、后根、和层次遍历

先根:先访问根结点,在访问叶子结点;后根:先访问叶子,再访问根

  • 树的先根遍历等价于二叉树的先序遍历

  • 树的后根遍历等价于二叉树的中序遍历

森林的遍历

总体分为先序、中序和后序

  • 森林的先序 = 二叉的先序
  • 森林的中序 = 二叉的中序
  • 森林的后序 = 二叉的后序

3.应用

假设存储结构式孩子兄弟链表,来存储一般的树

typedef struct CSNode{
    Elem data;
    struct CSNode *firstchild.*nextsibling;
}CSNode.*CSTree;

a.求树的深度

int TreeDepth(CSTree T){
    if(!T) return 0;
    else{
        h1 = TreeDepth(T->firstchild);
        h2 = TreeDepth(T->nextsibling);
        return (max(h1+1,h2));
    }
}

b.输出树中所有从根到叶子的路径

基本原理:

若不空:一直沿着左链走进栈,直到左子是空。判断有没有右

左空:代表是叶子结点,打印路径,并出栈

左空,没有右子树:退回到上一结点

左空,有右子树:将右子树入栈,再判断

//使用栈
void ALLPath(Bitree T,stack &S){
    if(T){
        //进栈;
        if(!T->lchild) //打印栈的元素(栈底到栈顶)
        else{
            ALLPath(T->lchild,S);
            ALLPath(T->rchild,S);
        }
        //出栈
    }
}

八、哈夫曼树

1.相关概念

  • 结点路径:树中一个结点到另一个结点之间的分支构成的路径eg:AEF
  • 路径长度:结点路径上的分支树
  • 树的路径长度:根结点到每一个叶子的路径长度的和
  • 结点的带权路径长度:根结点到结点之间的路劲长度于结点权值的乘积
  • 树的带权路径长度:根到每一个叶子的带权路径长度的和

哈夫曼(Huffman)树,一颗带权路径长度最小的二叉树(最优树),没有度为1的结点

2.哈夫曼树的构造

简单的几个规定:权小左,权大右,权值相等浅为左

n个叶子,总结点 2n - 1(n + (n - 1))个

构造方法

  • 从n个里面找最小的两个,合并成一颗二叉树,
  • 其根结点的权值 = 两个小叶子权值的和
  • 去除这两个小结点
  • 新根作为新结点
  • 直到只剩下一个结点

推荐使用静态链表来实现

静态链表

//获取两个最小值
int *Select(HuffmanTree HT,int n){
	int s1,s2;////最小值与极小值 
	int mmin =  Max_S;//先等于无穷大 
	int min = Max_S;
    for(int i = 1;i <= n;i++){
        if(HT[i].parent != 0) continue;//双亲不为零代表已经构成新树,应去除
        else{
            if(mmin >= HT[i].weight){//最小的
                min = mmin ;
                s2 = s1;
                mmin = HT[i].weight;
                s1 = i;
            }else if(min >= HT[i].weight){//次小的 
                min = HT[i].weight;
                s2 = i; 
            }
        }
    }
    //若最小值相等,则浅(先遍历的)树的序号在前 
    int S1 = qiushendu(HT,s1);
    int S2 = qiushendu(HT,s2);
    if(S1 >= S2){
    	int temp;
    	temp = s1; s1 = s2; s2 = temp;//交换顺序 
	}
	//用数组存放最小值和次小值 
    int a[3] = {0,s1,s2};
    return a;
}

//构建哈夫曼树 
void CreatHuffmanTree(HuffmanTree &HT,char *huf,int *wei,int n){
    int s1,s2;//最小值与极小值 
    if(n <= 1){
        cout << "抱歉,您输入的结点数不符合逻辑";
        return;
    }
    int m = 2*n-1;//总结点树
    HT = new HTNode[m+1];//放弃下标为零的数组
    //先遍历前n个数,初始化
    for(int i = 1;i <= m;i++){//全部初始化为零
        HT[i].parent = 0;
        HT[i].lchild = 0;
        HT[i].rchild = 0;
    }
  
	//赋结点、权值 
    for(int i;i <= n;i++){
    	HT[i].data = huf[i];//结点 
    	HT[i].weight = wei[(int)huf[i]];//权值 
    
	}
    //遍历后n+1个,新根
    for(int i = n+1;i <= m;i++){
        //找出最小额两个结点
        int *a;//获取s1和s2 
		a = Select(HT,i-1);//在n个数中找
		s1 = a[1]; s2 = a[2];
        //更新各个数值
        HT[s1].parent = i;
        HT[s2].parent = i;   
        HT[i].lchild = s1;
        HT[i].rchild = s2; 
        HT[i].weight =  HT[s1].weight + HT[s2].weight;  
    }

3.哈夫曼编码

1.引入

为提高传输速度,要求编码尽可能短,还要保证任意字符的编码都不是另一个字符编码的前缀,即前缀编码。Huffman树可以用来构造长度不等且不产生二义性的编码。

那该怎么解析(译码)呢?

从根结点出发走一条从跟到叶子的路径过程(遇0向左,遇1向右),达到叶子结点就译出一个字符,直至译码完成

2.哈夫曼的构造

基本思想:概率大的字符用短码,小的用长码,构造哈夫曼树,像下图那样(权值即频率)

3.相关结论

  • 哈夫曼编码是不等长的编码
  • 哈夫曼树,没有度为一的结点
  • 发送:根据哈夫曼树得到的编码表送出字符数据
  • 接收:按左0右1的规定,从根到叶子的遍历

有关数据结构基础—树与二叉树(2)的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  4. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  5. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  6. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  7. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  8. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  9. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  10. ruby-on-rails - 创建 ruby​​ 数据库时惰性符号绑定(bind)失败 - 2

    我正在尝试在Rails上安装ruby​​,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf

随机推荐