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 二叉链表 表示法

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

存储结构

结点数据类型定义

typedef struct BiTNode
{
int data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

2.2.2 三叉链表 表示法

存储结构

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

结点数据类型定义

//三叉链表
typedef struct TriTNode
{
int data;
//左右孩子指针
struct TriTNode *lchild, *rchild;
struct TriTNode *parent;
}TriTNode, *TriTree;

2.2.3 双亲 表示法

存储结构

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

结点数据类型定义

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

2.3 二叉树的遍历

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

遍历方法

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

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

 DLR                 LDR                LRD

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

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

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

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

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

2.3.1 先序遍历

PreOrder(NODE *root )
{
if (root) //非空二叉树
{
printf(“%d”,root->data); //访问D
PreOrder(root->lchild); //递归遍历左子树
PreOrder(root->rchild); //递归遍历右子树
}
}

2.3.2 中序遍历

InOrder(NODE *root)
{
if(root !=NULL)
{
InOrder(root->lchild);
printf(“%d”,root->data);
InOrder(root->rchild);
}
}

2.3.3 后序遍历

PostOrder(NODE *root)
{
if(root !=NULL)
{
PostOrder(root->lchild);
PostOrder(root->rchild);
printf(“%d”,root->data);
}
}

2.3.4 三种遍历的本质

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

XXX (NODE *root)
{
if(root)
{
XXX(root->lchild);
XXX(root->rchild);
}
}

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

从虚线的出发点到终点的路径上,每个结点经过 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 # #,请画出树的形状

解:

# 号法编程实践

利用前序遍历来建树

Bintree createBTpre( )
{ Bintree T; char ch;
scanf(“%c”,&ch);
if(ch==’#’) T=NULL;
else
{ T=( Bintree )malloc(sizeof(BinTNode));
T->data=ch;
T->lchild=createBTpre();
T->rchild=createBTpre();
}
return T;
}

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

void  BiTree_Free(BiTNode* T)
{
BiTNode *tmp = NULL;
if (T!= NULL)
{
if (T->rchild != NULL) BiTree_Free(T->rchild);
if (T->lchild != NULL) BiTree_Free(T->lchild);
if (T != NULL)
{
free(T);
T = NULL;
}
}
}

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. 达梦产品技术支持培训-day7-DM8数据库备份与还原-原理

    (本文部分内容摘自DM产品技术支持培训文档,如需要更详细的文档,请查询官方操作手册,谢谢) 1.DM8备份还原简介 1.1.基本概念 (1)表空间与数据文件 ▷ DM8表空间类型: ▷ SYSTEM ...

  2. 洛谷UVA524 素数环 Prime Ring Problem

    标签:搜索与回溯 题目: 从1到20这20个数摆成一个环,要求相邻的两个数的和是一个素数. 算法分析: 非常明显,这是一道回溯的题目.从1开始,每个空位有20种可能,只要填进去的数合法:与前面的数不相 ...

  3. Docker 启动容器时,报错 WARNING:IPv4 forwarding is disabled. Networking will not work. 的解决办法

    Centos 7 Docker 启动了一个web服务 但是启动时 报 WARNING: IPv4 forwarding is disabled. Networking will not work. 解 ...

  4. TP5上传图片到七牛云,并且删除七牛云的图片

    一,通过composer 下载七牛云 sdk composer require qiniu/php-sdk 二,手动下载七牛云sdk 1,https://developer.qiniu.com/kod ...

  5. linux 虚拟机下 安装redis

    虚拟机安装linux,打开,挂起就好: 使用ssh连接,这里使用的是Moba Xterm 可以ssh 可以ftp  满足你的日常开发所需,开发必备.每个人都有自己顺手的工具,你喜欢就好 虚拟机挂一边就 ...

  6. day25 Pyhton学习 MD5加密.日志

    一.MD5加密 MD5是一种不可逆的加密算法. 它是可靠的. 并且安全的. 在python中我们不需要手写这一套算法. 只需要引入一个叫hashlib的模块就能搞定MD5的加密工作 import ha ...

  7. oh my zsh 常用插件

    date: "2020-10-18T12:36:00+08:00" title: "oh my zsh 常用插件" tags: ["zsh" ...

  8. lumen单元测试

    phpunit --filter testInfo  tests/UserTest.php UserTest.php <?php use Laravel\Lumen\Testing\Databa ...

  9. ansible用authorized_key模块批量推送密钥到受控主机(免密登录)(ansible2.9.5)

    一,ansible的authorized_key模块的用途 用来配置密钥实现免密登录: ansible所在的主控机生成密钥后,如何把公钥上传到受控端? 当然可以用ssh-copy-id命令逐台手动处理 ...

  10. requirements基本使用

    requirements作用描述:很多 Python 项目中经常会包含一个 requirements.txt 文件,里面内容是项目的依赖包及其对应版本号的信息列表,即项目依赖关系清单,其作用是用来重新 ...