数据结构 | 二叉树的一些性质及证明、树的路径长度、结点的路径长度
一、树的介绍
1. 树的定义
树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。
把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
(01) 每个节点有零个或多个子节点;
(02) 没有父节点的节点称为根节点;
(03) 每一个非根节点有且只有一个父节点;
(04) 除了根节点外,每个子节点可以分为多个不相交的子树。
2. 树的基本术语
若一个结点有子树,那么该结点称为子树根的"双亲",子树的根是该结点的"孩子"。有相同双亲的结点互为"兄弟"。一个结点的所有子树上的任何结点都是该结点的后裔。从根结点到某个结点的路径上的所有结点都是该结点的祖先。
结点的度:结点拥有的子树的数目。
叶子:度为零的结点。
分支结点:度不为零的结点。
树的度:树中结点的最大的度。
层次:根结点的层次为1,其余结点的层次等于该结点的双亲结点的层次加1。
树的高度:树中结点的最大层次。
无序树:如果树中结点的各子树之间的次序是不重要的,可以交换位置。
有序树:如果树中结点的各子树之间的次序是重要的, 不可以交换位置。
森林:0个或多个不相交的树组成。对森林加上一个根,森林即成为树;删去根,树即成为森林。
树中的深度、高度及层数相关概念
深度是从上往下数的,高度是从下往上数的,深度和高度都涉及到节点的层数
(经过学习发现,深度、高度概念在不同的教材中有不同的定义,主要看高度深度的初值为几,有的为0,有的为1)。
(1).定义一(初值为0):节点的深度是根节点到这个节点所经历的边的个数
节点的高度是该节点到叶子节点的最长路径(边数)
树的高度等于根节点的高度
具体如下图所示(参考1):
(2).定义二(初值为1):节点的深度是根节点到这个节点的最长路径上的节点数
节点的高度是该节点到最远叶子节点的最长路径上的节点数
具体如下图所示(参考2):
此外还有最小深度的概念。最小深度是从根节点到最近叶子节点的最短路径上的节点数量(初值为1)。
二、二叉树的介绍
1. 二叉树的定义
二叉树是每个节点最多有两个子树的树结构。它有五种基本形态:
二叉树可以是空集;根可以有空的左子树或右子树;或者左、右子树皆为空。
2. 二叉树的性质
二叉树有以下几个性质:
性质1:二叉树第i层上的结点数目最多为 2{i-1} (i≥1)。
性质2:深度为k的二叉树至多有2{k}-1个结点(k≥1)。
性质3:包含n个结点的二叉树的高度至少为log2 (n+1)。
性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1。
2.1 性质1:二叉树第i层上的结点数目最多为 2{i-1} (i≥1)
证明:下面用"数学归纳法"进行证明。
(01) 当i=1时,第i层的节点数目为2{i-1}=2{0}=1。因为第1层上只有一个根结点,所以命题成立。
(02) 假设当i>1,第i层的节点数目为2{i-1}。这个是根据(01)推断出来的!
下面根据这个假设,推断出"第(i+1)层的节点数目为2{i}"即可。
由于二叉树的每个结点至多有两个孩子,故"第(i+1)层上的结点数目" 最多是 "第i层的结点数目的2倍"。即,第(i+1)层上的结点数目最大值=2×2{i-1}=2{i}。
故假设成立,原命题得证!
2.2 性质2:深度为k的二叉树至多有2{k}-1个结点(k≥1)
证明:在具有相同深度的二叉树中,当每一层都含有最大结点数时,其树中结点数最多。利用"性质1"可知,深度为k的二叉树的结点数至多为:
20+21+…+2k-1=2k-1
故原命题得证!
2.3 性质3:包含n个结点的二叉树的高度至少为log2 (n+1)
证明:根据"性质2"可知,高度为h的二叉树最多有2{h}–1个结点。反之,对于包含n个节点的二叉树的高度至少为log2(n+1)。
2.4 性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1
证明:因为二叉树中所有结点的度数均不大于2,所以结点总数(记为n)="0度结点数(n0)" + "1度结点数(n1)" + "2度结点数(n2)"。由此,得到等式一。
(等式一) n=n0+n1+n2
另一方面,0度结点没有孩子,1度结点有一个孩子,2度结点有两个孩子,故二叉树中孩子结点总数是:n1+2n2。此外,只有根不是任何结点的孩子。故二叉树中的结点总数又可表示为等式二。
(等式二) n=n1+2n2+1
由(等式一)和(等式二)计算得到:n0=n2+1。原命题得证!
3. 满二叉树,完全二叉树和二叉查找树
3.1 满二叉树
定义:高度为h,并且由2{h} –1个结点的二叉树,被称为满二叉树。
3.2 完全二叉树
定义:一棵二叉树中,只有最下面两层结点的度可以小于2,并且最下一层的叶结点集中在靠左的若干位置上。这样的二叉树称为完全二叉树。
特点:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。显然,一棵满二叉树必定是一棵完全二叉树,而完全二叉树未必是满二叉树。
3.3 二叉查找树
定义:二叉查找树(Binary Search Tree),又被称为二叉搜索树。设x为二叉查找树中的一个结点,x节点包含关键字key,节点x的key值记为key[x]。如果y是x的左子树中的一个结点,则key[y] <= key[x];如果y是x的右子树的一个结点,则key[y] >= key[x]。
在二叉查找树中:
(01) 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(02) 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(03) 任意节点的左、右子树也分别为二叉查找树。
(04) 没有键值相等的节点(no duplicate nodes)。
在实际应用中,二叉查找树的使用比较多。下面,用C语言实现二叉查找树。
二叉查找树的C实现
1. 节点定义
1.1 节点定义
typedef int Type;typedef struct BSTreeNode{Type key; // 关键字(键值)struct BSTreeNode *left; // 左孩子struct BSTreeNode *right; // 右孩子struct BSTreeNode *parent; // 父结点
}Node, *BSTree;
二叉查找树的节点包含的基本信息:
(01) key -- 它是关键字,是用来对二叉查找树的节点进行排序的。
(02) left -- 它指向当前节点的左孩子。
(03) right -- 它指向当前节点的右孩子。
(04) parent -- 它指向当前节点的父结点。
1.2 创建节点
创建节点的代码
static Node* create_bstree_node(Type key, Node *parent, Node *left, Node* right)
{Node* p;if ((p = (Node *)malloc(sizeof(Node))) == NULL)return NULL;p->key = key;p->left = left;p->right = right;p->parent = parent;return p;
}
2 遍历
这里讲解前序遍历、中序遍历、后序遍历3种方式。
2.1 前序遍历
若二叉树非空,则执行以下操作:
(01) 访问根结点;
(02) 先序遍历左子树;
(03) 先序遍历右子树。
前序遍历代码
void preorder_bstree(BSTree tree) {if(tree != NULL){printf("%d ", tree->key);preorder_bstree(tree->left);preorder_bstree(tree->right);} }
2.2 中序遍历
若二叉树非空,则执行以下操作:
(01) 中序遍历左子树;
(02) 访问根结点;
(03) 中序遍历右子树。
中序遍历代码
void inorder_bstree(BSTree tree) {if(tree != NULL){inorder_bstree(tree->left);printf("%d ", tree->key);inorder_bstree(tree->right);} }
2.3 后序遍历
若二叉树非空,则执行以下操作:
(01) 后序遍历左子树;
(02) 后序遍历右子树;
(03) 访问根结点。
后序遍历代码
void postorder_bstree(BSTree tree) {if(tree != NULL){postorder_bstree(tree->left);postorder_bstree(tree->right);printf("%d ", tree->key);} }
下面通过例子对这些遍历方式进行介绍。
对于上面的二叉树而言,
(01) 前序遍历结果: 3 1 2 5 4 6
(02) 中序遍历结果: 1 2 3 4 5 6
(03) 后序遍历结果: 2 1 4 6 5 3
3. 查找
递归版本的代码
Node* bstree_search(BSTree x, Type key)
{if (x==NULL || x->key==key)return x;if (key < x->key)return bstree_search(x->left, key);elsereturn bstree_search(x->right, key);
}
非递归版本的代码
Node* iterative_bstree_search(BSTree x, Type key)
{while ((x!=NULL) && (x->key!=key)){if (key < x->key)x = x->left;elsex = x->right;}return x;
}
4. 最大值和最小值
查找最大值的代码
Node* bstree_maximum(BSTree tree) {if (tree == NULL)return NULL;while(tree->right != NULL)tree = tree->right;return tree; }
查找最小值的代码
Node* bstree_minimum(BSTree tree) {if (tree == NULL)return NULL;while(tree->left != NULL)tree = tree->left;return tree; }
5. 前驱和后继
节点的前驱:是该节点的左子树中的最大节点。
节点的后继:是该节点的右子树中的最小节点。
查找前驱节点的代码
Node* bstree_predecessor(Node *x)
{// 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。if (x->left != NULL)return bstree_maximum(x->left);// 如果x没有左孩子。则x有以下两种可能:// (01) x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。// (01) x是"一个左孩子",则查找"x的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。Node* y = x->parent;while ((y!=NULL) && (x==y->left)){x = y;y = y->parent;}return y;
}
查找后继节点的代码
Node* bstree_successor(Node *x)
{// 如果x存在右孩子,则"x的后继结点"为 "以其右孩子为根的子树的最小结点"。if (x->right != NULL)return bstree_minimum(x->right);// 如果x没有右孩子。则x有以下两种可能:// (01) x是"一个左孩子",则"x的后继结点"为 "它的父结点"。// (02) x是"一个右孩子",则查找"x的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"x的后继结点"。Node* y = x->parent;while ((y!=NULL) && (x==y->right)){x = y;y = y->parent;}return y;
}
6. 插入
插入节点的代码
static Node* bstree_insert(BSTree tree, Node *z)
{Node *y = NULL;Node *x = tree;// 查找z的插入位置while (x != NULL){y = x;if (z->key < x->key)x = x->left;elsex = x->right;}z->parent = y;if (y==NULL)tree = z;else if (z->key < y->key)y->left = z;elsey->right = z;return tree;
}Node* insert_bstree(BSTree tree, Type key)
{Node *z; // 新建结点// 如果新建结点失败,则返回。if ((z=create_bstree_node(key, NULL, NULL, NULL)) == NULL)return tree;return bstree_insert(tree, z);
}
bstree_insert(tree, z)是内部函数,它的作用是:将结点(z)插入到二叉树(tree)中,并返回插入节点后的根节点。
insert_bstree(tree, key)是对外接口,它的作用是:在树中新增节点,key是节点的值;并返回插入节点后的根节点。
注:本文实现的二叉查找树是允许插入相同键值的节点的!若用户不希望插入相同键值的节点,将bstree_insert()修改为以下代码即可。
static Node* bstree_insert(BSTree tree, Node *z)
{Node *y = NULL;Node *x = tree;// 查找z的插入位置while (x != NULL){y = x;if (z->key < x->key)x = x->left;else if (z->key > x->key)x = x->right;else{free(z); // 释放之前分配的系统。return tree;}}z->parent = y;if (y==NULL)tree = z;else if (z->key < y->key)y->left = z;elsey->right = z;return tree;
}
7. 删除
删除节点的代码
static Node* bstree_delete(BSTree tree, Node *z)
{Node *x=NULL;Node *y=NULL;if ((z->left == NULL) || (z->right == NULL) )y = z;elsey = bstree_successor(z);if (y->left != NULL)x = y->left;elsex = y->right;if (x != NULL)x->parent = y->parent;if (y->parent == NULL)tree = x;else if (y == y->parent->left)y->parent->left = x;elsey->parent->right = x;if (y != z) z->key = y->key;if (y!=NULL)free(y);return tree;
}Node* delete_bstree(BSTree tree, Type key)
{Node *z, *node; if ((z = bstree_search(tree, key)) != NULL)tree = bstree_delete(tree, z);return tree;
}
bstree_delete(tree, z)是内部函数,它的作用是:删除二叉树(tree)中的节点(z),并返回删除节点后的根节点。
delete_bstree(tree, key)是对外接口,它的作用是:在树中查找键值为key的节点,找到的话就删除该节点;并返回删除节点后的根节点。
8. 打印
打印二叉树的代码
void print_bstree(BSTree tree, Type key, int direction) {if(tree != NULL){if(direction==0) // tree是根节点printf("%2d is root\n", tree->key);else // tree是分支节点printf("%2d is %2d's %6s child\n", tree->key, key, direction==1?"right" : "left");print_bstree(tree->left, tree->key, -1);print_bstree(tree->right,tree->key, 1);} }
print_bstree(tree, key, direction)的作用是打印整颗二叉树(tree)。其中,tree是二叉树节点,key是二叉树的键值,而direction表示该节点的类型:
direction为 0,表示该节点是根节点;
direction为-1,表示该节点是它的父结点的左孩子;
direction为 1,表示该节点是它的父结点的右孩子。
9. 销毁二叉树
销毁二叉树的代码
void destroy_bstree(BSTree tree) {if (tree==NULL)return ;if (tree->left != NULL)destroy_bstree(tree->left);if (tree->right != NULL)destroy_bstree(tree->right);free(tree); }
转载出处:
二叉查找树(一)之 图文解析 和 C语言的实现 - 如果天空不死 - 博客园
树的高度与深度 - jianglin_liu - 博客园
数据结构 | 二叉树的一些性质及证明、树的路径长度、结点的路径长度相关推荐
- 数据结构——二叉树——特点及性质
数据结构--二叉树--特点及性质 二叉树(Binary Tree)是n(n=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的.分别称为根结点的左子树和右子树的二 ...
- 二叉树的基本性质及证明
性质1:一棵非空二叉树的第i层上最多有2^(i-1)个结点,(i>=1). 性质2:一棵深度为k的二叉树中,最多具有2^k-1个结点,最少有k个结点. 性质3:对于一棵非空的二叉树,度为0的结点 ...
- c++用二叉树表示代数表达式_C语言:数据结构-二叉树的定义和基本术语和二叉树的性质...
二叉树的定义和基本术语 (1)二叉树的定义 二叉树(Binary Tree):每个结点至多有两棵子树,且子树有左.右之分.在二叉树中不含度数大于2的结点. 二叉树的递归定义为:二叉树或者是一棵空树,或 ...
- 我的软考之路(四)——数据结构与算法(2)之树与二叉树
上篇博文主要介绍的是数据结构的线性结构,我们这篇博文介绍非线性结构-树与二叉树,我先介绍树的一些基本概念,树的遍历,再介绍二叉树相关概念和特性,以及二叉树的遍历,最后再树与二叉树的对比,总结. 树为了 ...
- 树、二叉树、满二叉树、完全二叉树、二叉树的重要性质及其存储结构
树的概念及结构 树的概念 树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成的一个具有层次关系的集合.把它叫做"树",是因为它看起来像一颗倒挂的树,也就是说它是根 ...
- 3. 数据结构--二叉树 BST AVL树 Huffman
数据结构–二叉树 KEY:(不敢相信没有堆-) 二叉树的定义及其主要特征 ☑️ 二叉树的顺序存储结构和链式存储结构实现 二叉树的遍历及应用 二叉排序(查找.检索)树 (BST) 平衡的二叉检索树- A ...
- 数据结构(十一) -- C语言版 -- 树 - 二叉树基本概念
内容预览 零.读前说明 一.二叉树相关概念 1.1.定义 1.2.性质 1.3.满二叉树与完全二叉树 1.3.1.满二叉树 1.3.2.完全二叉树 1.3.3.特点延伸 二.二叉树储存结构 2.1.顺 ...
- java 二叉树 红黑树_常见数据结构(二)-树(二叉树,红黑树,B树)
常见数据结构(二)-树(二叉树,红黑树,B树) 标签: algorithms [TOC] 本文介绍数据结构中几种常见的树:二分查找树,2-3树,红黑树,B树 写在前面 本文所有图片均截图自course ...
- 数据结构(C++)笔记:05.树和二叉树
文章目录 5.1 树的逻辑结构 5.1.1 树的定义和基本术语 5.1.2 树的抽象数据类型定义 5.1.3 树的遍历操作 5.2 树的存储结构 5.2.1 双亲表示法 5.2.2 孩子表示法 5.2 ...
最新文章
- 一个牛逼的 多级缓存 实现方案!
- 如何彻底解决pip install慢的问题
- WINCE的内存配置
- asp.net 中ListBox 显示 2 列
- 以太坊智能合约安全入门了解一下(下)
- php data类型转换,【原】超简单类型转换(DataTable
- append()与extend()
- php asp写法,asp/php常用的库连接文件代码写法大全
- 2019中国民营企业500强发布:华为、海航、苏宁位列前三
- MAC环境配置SDK
- Snagit 截图不清晰问题解决
- 吴恩达深度学习环境配置
- 2021考研数学二汤家凤接力题典1800
- 离散数学与组合数学汇总
- 高中计算机学考题库,高中信息技术学业水平考试试题汇总(含答案)
- 为什么在不同网站查询本机的公网IP不一样?
- 深入理解java虚拟机之——垃圾回收(垃圾判断,垃圾收集算法,垃圾收集器)
- IDEA 安装快捷键提示工具:Key promoter X
- 【MySQL】SQL优化
- 如何使用predict()输出预测结果 以及 输出值转换为0/1值。
热门文章
- leetcode377与leetcode518
- 最新Android SDK r23
- C4D R23-R19 安装包(winmac)安装教程及资源文件
- 体验 RAD Studio C++ Builder 11.1.5
- 新兴网络市场-房地产.(reposted)
- 基于51单片机的电动车防盗系统proteus仿真
- 此生不戒多巴胺-冲刺日志(第二天)
- AT+CREG AT+CREG
- 香蕉派M1,R1最新内核,通用版本正式发布,欢迎下载测试
- Cesium距离测量之思路解析加源码