1 树

1.1 定义

由一个或多个(n ≥ 0)结点组成的有限集合 T,有且仅有一个结点称为根(root),当 n > 1 时,其余的结点分为 m (m ≥ 0)个互不相交的有限集合T1,T2,…,Tm。每个集合本身又是棵树,被称作这个根的子树 。

1.2 结构特点

  • 非线性结构,有一个直接前驱,但可能有多个直接后继(1:n)

  • 树的定义具有递归性,树中还有树。

  • 树可以为空,即节点个数为0。

1.3 术语

  • 根:即根结点(没有前驱)

  • 叶子:即终端结点(没有后继)

  • 森林:指m棵不相交的树的集合(例如删除A后的子树个数)

  • 有序树:结点各子树从左至右有序,不能互换(左为第一)

  • 无序树:结点各子树可互换位置。

  • 双亲:即上层的那个结点(直接前驱) parent

  • 孩子:即下层结点的子树 (直接后继) child

  • 兄弟:同一双亲下的同层结点(孩子之间互称兄弟)sibling

  • 堂兄弟:即双亲位于同一层的结点(但并非同一双亲)cousin

  • 祖先:即从根到该结点所经分支的所有结点

  • 子孙:即该结点下层子树中的任一结点

  • 结点:即树的数据元素

  • 结点的度:结点挂接的子树数(有几个直接后继就是几度)

  • 结点的层次:从根到该结点的层数(根结点算第一层)

  • 终端结点:即度为 0 的结点,即叶子

  • 分支结点:除树根以外的结点(也称为内部结点)

  • 树的度:所有结点度中的最大值(Max{各结点的度})

  • 树的深度(或高度):指所有结点中最大的层数(Max{各结点的层次})

上图中的结点数= 13,树的度= 3,树的深度= 4

1.4 树的表示法

1.4.1 广义表表示法

用广义表表示法表示上图:

中国(河北(保定,石家庄),广东(广州,东莞),山东(青岛,济南))

根作为由子树森林组成的表的名字写在表的左边

1.4.2 左孩子右兄弟表示法

左孩子右兄弟表示法可以将一颗多叉树转化为一颗二叉树

节点的结构

节点有两个指针域,其中一个指针指向子节点,另一个指针指向其兄弟节点。

1.5 树的结构

1.5.1 逻辑结构

一对多(1:n),有多个直接后继(如家谱树、目录树等等),但只有一个根结点,且子树之间互不相交。

1.5.2 存储结构

树的存储仍然有两种方式:

  • 顺序存储

可规定为:从上至下、从左至右将树的结点依次存入内存。

重大缺陷:复原困难(不能唯一复原就没有实用价值)。

  • 链式存储

可用多重链表:一个前趋指针,n个后继指针。

细节问题:树中结点的结构类型样式该如何设计?

即应该设计成“等长”还是“不等长”?

缺点:等长结构太浪费(每个结点的度不一定相同);

不等长结构太复杂(要定义好多种结构类型)。

以上两种存储方式都存在重大缺陷,应该如何解决呢?

计算机实现各种不同进制的运算是通过先研究最简单、最有规律的二进制运算规律,然后设法把各种不同进制的运算转化二进制运算。树的存储也可以通过先研究最简单、最有规律的树,然后设法把一般的树转化为这种简单的树,这种树就是 二叉树

2 二叉树

2.1 定义

n(n ≥ 0)个结点的有限集合,由 一个根结点 以及 两棵互不相交的、分别称为左子树和右子树的 二叉树 组成 。

2.2 逻辑结构

一对二(1:2)

2.3 基本特征

  • 每个结点最多只有两棵子树(不存在度大于2的结点)

  • 左子树和右子树次序不能颠倒(有序树)

2.4 基本形态

2.5 二叉树的性质

  • 性质1: 在二叉树的第 i 层上至多有 \({2^{i-1}}\) 个结点(i > 0)

  • 性质2: 深度为 k 的二叉树至多有 \({2^{k-1}}\) 个结点(k > 0)

  • 性质3: 对于任何一棵二叉树,若 2 度的结点数有 n2 个,则叶子数(n0)必定为 n2+1 (即 n0 = n2 + 1)

满二叉树:一棵深度为k 且有 \({2^{k-1}}\) 个结点的二叉树。

特点:每层都“充满”了结点

完全二叉树:深度为 k 的,有 n 个结点的二叉树,当且仅当其每一个结点都与深度为 k 的满二叉树中编号从 1 至 n 的结点一一对应。

  • 性质4: 具有 n 个结点的完全二叉树的深度必为 \({log_{2}n} +{1}\)

  • 性质5: 对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点,其左孩子编号必为 2i,其右孩子编号必为 2i+1。其双亲的编号必为i/2(i=1 时为根,除外)

使用此性质可以使用完全二叉树实现树的顺序存储。

2.2 二叉树的表示

2.2.1 二叉链表 表示法

一般从根结点开始存储。相应地,访问树中结点时也只能从根开始。

存储结构

结点数据类型定义

  1. typedef struct BiTNode
  2. {
  3. int data;
  4. struct BiTNode *lchild, *rchild;
  5. }BiTNode, *BiTree;

2.2.2 三叉链表 表示法

存储结构

每个节点有三个指针域,其中两个分别指向子节点(左孩子,右孩子),还有一共指针指向该节点的父节点。

结点数据类型定义

  1. //三叉链表
  2. typedef struct TriTNode
  3. {
  4. int data;
  5. //左右孩子指针
  6. struct TriTNode *lchild, *rchild;
  7. struct TriTNode *parent;
  8. }TriTNode, *TriTree;

2.2.3 双亲 表示法

存储结构

每个节点都由一个数据结构组成,每个节点顺序排放在数组中。

结点数据类型定义

  1. //双亲链表
  2. #define MAX_TREE_SIZE 100
  3. typedef struct BPTNode
  4. {
  5. int data; // 数据
  6. int parentPosition; //指向双亲的指针,数组下标
  7. char LRTag; //左右孩子标志域
  8. }BPTNode;
  9. typedef struct BPTree
  10. {
  11. //因为节点之间是分散的,需要把节点存储到数组中
  12. BPTNode nodes[100];
  13. int num_node; //节点数目
  14. //根结点的位置,注意此域存储的是父亲节点在数组的下标
  15. int root;
  16. }BPTree;

2.3 二叉树的遍历

定义:指按某条搜索路线 遍访每个结点且不重复

遍历方法

牢记一种约定,对每个结点的查看都是“先左后右” 。

限定先左后右,树的遍历有三种实现方案:

  1. DLR LDR LRD

先 (根)序遍历 中 (根)序遍历 后(根)序遍历

  • DLR:先序遍历,即先根再左再右

  • LDR:中序遍历,即先左再根再右

  • LRD: 后序遍历,即先左再右再根

注:“先、中、后”的意思是指访问的结点 D 是先于子树出现还是后于子树出现。

2.3.1 先序遍历

  1. PreOrder(NODE *root )
  2. {
  3. if (root) //非空二叉树
  4. {
  5. printf(“%d”,root->data); //访问D
  6. PreOrder(root->lchild); //递归遍历左子树
  7. PreOrder(root->rchild); //递归遍历右子树
  8. }
  9. }

2.3.2 中序遍历

  1. InOrder(NODE *root)
  2. {
  3. if(root !=NULL)
  4. {
  5. InOrder(root->lchild);
  6. printf(“%d”,root->data);
  7. InOrder(root->rchild);
  8. }
  9. }

2.3.3 后序遍历

  1. PostOrder(NODE *root)
  2. {
  3. if(root !=NULL)
  4. {
  5. PostOrder(root->lchild);
  6. PostOrder(root->rchild);
  7. printf(“%d”,root->data);
  8. }
  9. }

2.3.4 三种遍历的本质

从前面的三种遍历算法可以知道:如果将printf语句抹去,除去printf的遍历算法:

  1. XXX (NODE *root)
  2. {
  3. if(root)
  4. {
  5. XXX(root->lchild);
  6. XXX(root->rchild);
  7. }
  8. }

从递归的角度看,这三种算法是完全相同的,或者说这三种遍历算法的访问路径是相同的,只是访问结点的时机不同。

从虚线的出发点到终点的路径上,每个结点经过 3 次。

  • 第 1 次经过时访问=先序遍历

  • 第 2 次经过时访问=中序遍历

  • 第 3 次经过时访问=后序遍历

2.4 二叉树的创建

2.4.1 先序和中序 创建树

算法

  • 通过先序遍历找到根结点A,再通过A在中序遍历的位置找出左子树,右子树

  • 在A的左子树中,找左子树的根结点(在先序中找),转步骤1

  • 在A的右子树中,找右子树的根结点(在先序中找),转步骤1

练习1

先序遍历结果:A D E B C F

中序遍历结果:D E A C F B

解:

练习2

先序遍历结果:A B D H K E C F I G J

中序遍历结果:H K D B E A I F C G J

解:

2.4.2 # 号法 创建树

什么是 # 号法创建树:# 创建树,让树的每一个节点都变成度数为 2 的树

例子1: 124###3##

解:

例子2:先序遍历:A B D H # K # # # E # # C F I # # # G # J # #,请画出树的形状

解:

# 号法编程实践

利用前序遍历来建树

  1. Bintree createBTpre( )
  2. { Bintree T; char ch;
  3. scanf(“%c”,&ch);
  4. if(ch==’#’) T=NULL;
  5. else
  6. { T=( Bintree )malloc(sizeof(BinTNode));
  7. T->data=ch;
  8. T->lchild=createBTpre();
  9. T->rchild=createBTpre();
  10. }
  11. return T;
  12. }

使用后序遍历的方式销毁一棵树, 先释放叶子节点,在释放根节点:

  1. void BiTree_Free(BiTNode* T)
  2. {
  3. BiTNode *tmp = NULL;
  4. if (T!= NULL)
  5. {
  6. if (T->rchild != NULL) BiTree_Free(T->rchild);
  7. if (T->lchild != NULL) BiTree_Free(T->lchild);
  8. if (T != NULL)
  9. {
  10. free(T);
  11. T = NULL;
  12. }
  13. }
  14. }

3 霍夫曼树

3.1 概念

组建一个网络,耗费最小 WPL(树的带权路径长度)最小,称为 霍夫曼树。

从树中一个节点到另一个节点之间的分支构成两个节点之间的路径,路径上的分支数目称作 路径长度

例子

如下图的二叉树 a 中,根节点到节点 D 的路径长度为 4,二叉树 b 中根节点到节点 D 的路径长度为 2。树的路径长度就是从树根到每一节点的路径长度之和。二叉树 a 的树路径长度就为 1+1+2+2+3+3+4+4=20。二叉树 b 的树路径长度为 1+2+3+3+2+1+2+2=16。

3.2 霍夫曼树的构造

对于文本”BADCADFEED”的传输而言,因为重复出现的只有“ABCDEF”这6个字符,因此可以用下面的方式编码:

A B C D E F
000 001 010 011 100 101

B A D C A D F E E D 001 000 011 010 000 011 101 100 100 011

接收方可以根据每 3 个 bit 进行一次字符解码的方式还原文本信息。这样的编码方式需要 30 个 bit 位才能表示 10 个字符那么当传输一篇 500 个字符的情报时,需要 15000 个 bit 位,在战争年代,这种编码方式对于情报的发送和接受是很低效且容易出错的。如何提高收发效率?

A B C D E F
01 1001 101 00 11 1000

B A D C A D F E E D 1001 01 00 101 01 00 1000 11 11 00

准则:任一字符的编码都不是另一个字符编码的前缀!

也就是说:每一个字符的编码路径,都不包含另外一个字符的路径。

3.2.1 构建规则

  • 给定 n 个数值{ v1, v2, …, vn}。

  • 根据这 n 个数值构造二叉树集合 F = { T1, T2, …, Tn},Ti 的数据域为 vi,左右子树为空。

  • 在 F 中选取两棵根结点的值最小的树作为左右子树构造一棵新的二叉树,这棵二叉树的根结点中的值为左右子树根结点中的值之和

  • 在 F 中删除这两棵子树,并将构造的新二叉树根节点加入 F 中

  • 重复 3 和4,直到 F 中只剩下一个树为止。

这棵树即 霍夫曼树

3.2.2 特点

  • 所用的字符都作为叶子节点出现。

  • 根到每个字符的路径都不重复,也不存在重叠的现象。

3.2.3 特点

  • 霍夫曼树是一种特殊的二叉树。

  • 霍夫曼树应用于信息编码和数据压缩领域。

  • 霍夫曼树是现代压缩算法的基础。

C++ 数据结构 3:树和二叉树的更多相关文章

  1. python数据结构之树和二叉树(先序遍历、中序遍历和后序遍历)

    python数据结构之树和二叉树(先序遍历.中序遍历和后序遍历) 树 树是\(n\)(\(n\ge 0\))个结点的有限集.在任意一棵非空树中,有且只有一个根结点. 二叉树是有限个元素的集合,该集合或 ...

  2. Java数据结构之树和二叉树(2)

    从这里始将要继续进行Java数据结构的相关讲解,Are you ready?Let's go~~ Java中的数据结构模型可以分为一下几部分: 1.线性结构 2.树形结构 3.图形或者网状结构 接下来 ...

  3. Java数据结构之树和二叉树

    从这里开始将要进行Java数据结构的相关讲解,Are you ready?Let's go~~ Java中的数据结构模型可以分为一下几部分: 1.线性结构 2.树形结构 3.图形或者网状结构 接下来的 ...

  4. 【PHP数据结构】树和二叉树

    树的概念其实非常地广泛,也非常地常见,大家见到这个词千万不要惊慌,因为真的每天你都能见到树结构在我们生活中的应用.比如说公司的组织结构: 另外像我们家里的族谱,或者说是我们的家庭结构,也是一个典型的树 ...

  5. 【Java】 大话数据结构(9) 树(二叉树、线索二叉树)

    本文根据<大话数据结构>一书,对Java版的二叉树.线索二叉树进行了一定程度的实现. 另: 二叉排序树(二叉搜索树) 平衡二叉树(AVL树) 二叉树的性质 性质1:二叉树第i层上的结点数目 ...

  6. python数据结构之树(二叉树的遍历)

    树是数据结构中非常重要的一种,主要的用途是用来提高查找效率,对于要重复查找的情况效果更佳,如二叉排序树.FP-树. 本篇学习笔记来自:二叉树及其七种遍历方式.python遍历与非遍历方式实现二叉树 介 ...

  7. javascript实现数据结构: 树和二叉树的应用--最优二叉树(赫夫曼树),回溯法与树的遍历--求集合幂集及八皇后问题

    赫夫曼树及其应用 赫夫曼(Huffman)树又称最优树,是一类带权路径长度最短的树,有着广泛的应用. 最优二叉树(Huffman树) 1 基本概念 ① 结点路径:从树中一个结点到另一个结点的之间的分支 ...

  8. javascript实现数据结构: 树和二叉树,二叉树的遍历和基本操作

    树型结构是一类非常重要的非线性结构.直观地,树型结构是以分支关系定义的层次结构. 树在计算机领域中也有着广泛的应用,例如在编译程序中,用树来表示源程序的语法结构:在数据库系统中,可用树来组织信息:在分 ...

  9. SDUST数据结构 - chap6 树与二叉树

    判断题: 选择题: 函数题: 6-1 求二叉树高度: 裁判测试程序样例: #include <stdio.h> #include <stdlib.h> typedef char ...

  10. 数据结构与算法系列研究五——树、二叉树、三叉树、平衡排序二叉树AVL

    树.二叉树.三叉树.平衡排序二叉树AVL 一.树的定义 树是计算机算法最重要的非线性结构.树中每个数据元素至多有一个直接前驱,但可以有多个直接后继.树是一种以分支关系定义的层次结构.    a.树是n ...

随机推荐

  1. 【MySQL Errors】Table 'xxx' is marked as crashed and should be repaired 的解决方案

    现象描述 访问 Zabbix Web,出现如下错误提示: • Error in query [SELECT * FROM history_uint h WHERE h.itemid='25067' O ...

  2. devops-jenkins分布式构建

    1. devops-jenkins分布式构建 1) 点击系统管理 进入节点管理 2) 点击新建节点 3) 进行节点配置在node节点服务器配置,进行java的安装 [root@nginx-backup ...

  3. MATLAB利用solve函数解多元一次方程组

    matlab求解多元方程组示例: syms k1 k2 k3; [k1 k2 k3] = solve(-3-k3==6, 2-k1-k2+2*k3==11, 2*k1+k2-k3+1==6)或者用[k ...

  4. ps 树形显示

    ps -axjf|grep swoft

  5. centos8使用timedatectl管理时间

    一,centos8中默认使用chronyd来做时间服务 1,查看chronyd服务的状态 [root@blog ~]# systemctl status chronyd ● chronyd.servi ...

  6. PHP出现access denied问题及解决办法

    配置好PHP环境后,访问页面,页面上只出现了 Access denied字样. 问题分析 打开 /usr/local/php/etc/php.ini 文件,找到 cgi.fix_pathinfo ; ...

  7. 第十二章 配置vlan

    一.vlan技术简介 1.广播风暴 广播风暴(broadcast storm)简单的讲是指当广播数据充斥网络无法处理,并占用大量网络带宽,导致正常业务不能运行,甚至彻底瘫痪,这就发生了"广播 ...

  8. python 微信小程序自动化

    微信小程序自动化 https://www.cnblogs.com/yyoba/python27 - FautoTesthttps://www.cnblogs.com/yyoba/p/9973731.h ...

  9. 如何玩转Python? 一文总结30种Python的窍门和技巧

    Python作为2019年必备语言之一,展现了不可替代作用.对于所有的数据科学工作者,如何提高使用Python的效率,这里,总结了30种Python的最佳实践.技巧和窍门.希望这些可以帮助大家在202 ...

  10. 「IDEA插件精选」安利一个IDEA骚操作:一键生成方法的序列图

    在平时的学习/工作中,我们会经常面临如下场景: 阅读别人的代码 阅读框架源码 阅读自己很久之前写的代码. 千万不要觉得工作就是单纯写代码,实际工作中,你会发现你的大部分时间实际都花在了阅读和理解已有代 ...