一、树的介绍

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 - 博客园

数据结构 | 二叉树的一些性质及证明、树的路径长度、结点的路径长度相关推荐

  1. 数据结构——二叉树——特点及性质

    数据结构--二叉树--特点及性质 二叉树(Binary Tree)是n(n=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的.分别称为根结点的左子树和右子树的二 ...

  2. 二叉树的基本性质及证明

    性质1:一棵非空二叉树的第i层上最多有2^(i-1)个结点,(i>=1). 性质2:一棵深度为k的二叉树中,最多具有2^k-1个结点,最少有k个结点. 性质3:对于一棵非空的二叉树,度为0的结点 ...

  3. c++用二叉树表示代数表达式_C语言:数据结构-二叉树的定义和基本术语和二叉树的性质...

    二叉树的定义和基本术语 (1)二叉树的定义 二叉树(Binary Tree):每个结点至多有两棵子树,且子树有左.右之分.在二叉树中不含度数大于2的结点. 二叉树的递归定义为:二叉树或者是一棵空树,或 ...

  4. 我的软考之路(四)——数据结构与算法(2)之树与二叉树

    上篇博文主要介绍的是数据结构的线性结构,我们这篇博文介绍非线性结构-树与二叉树,我先介绍树的一些基本概念,树的遍历,再介绍二叉树相关概念和特性,以及二叉树的遍历,最后再树与二叉树的对比,总结. 树为了 ...

  5. 树、二叉树、满二叉树、完全二叉树、二叉树的重要性质及其存储结构

    树的概念及结构 树的概念  树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成的一个具有层次关系的集合.把它叫做"树",是因为它看起来像一颗倒挂的树,也就是说它是根 ...

  6. 3. 数据结构--二叉树 BST AVL树 Huffman

    数据结构–二叉树 KEY:(不敢相信没有堆-) 二叉树的定义及其主要特征 ☑️ 二叉树的顺序存储结构和链式存储结构实现 二叉树的遍历及应用 二叉排序(查找.检索)树 (BST) 平衡的二叉检索树- A ...

  7. 数据结构(十一) -- C语言版 -- 树 - 二叉树基本概念

    内容预览 零.读前说明 一.二叉树相关概念 1.1.定义 1.2.性质 1.3.满二叉树与完全二叉树 1.3.1.满二叉树 1.3.2.完全二叉树 1.3.3.特点延伸 二.二叉树储存结构 2.1.顺 ...

  8. java 二叉树 红黑树_常见数据结构(二)-树(二叉树,红黑树,B树)

    常见数据结构(二)-树(二叉树,红黑树,B树) 标签: algorithms [TOC] 本文介绍数据结构中几种常见的树:二分查找树,2-3树,红黑树,B树 写在前面 本文所有图片均截图自course ...

  9. 数据结构(C++)笔记:05.树和二叉树

    文章目录 5.1 树的逻辑结构 5.1.1 树的定义和基本术语 5.1.2 树的抽象数据类型定义 5.1.3 树的遍历操作 5.2 树的存储结构 5.2.1 双亲表示法 5.2.2 孩子表示法 5.2 ...

最新文章

  1. 一个牛逼的 多级缓存 实现方案!
  2. 如何彻底解决pip install慢的问题
  3. WINCE的内存配置
  4. asp.net 中ListBox 显示 2 列
  5. 以太坊智能合约安全入门了解一下(下)
  6. php data类型转换,【原】超简单类型转换(DataTable
  7. append()与extend()
  8. php asp写法,asp/php常用的库连接文件代码写法大全
  9. 2019中国民营企业500强发布:华为、海航、苏宁位列前三
  10. MAC环境配置SDK
  11. Snagit 截图不清晰问题解决
  12. 吴恩达深度学习环境配置
  13. 2021考研数学二汤家凤接力题典1800
  14. 离散数学与组合数学汇总
  15. 高中计算机学考题库,高中信息技术学业水平考试试题汇总(含答案)
  16. 为什么在不同网站查询本机的公网IP不一样?
  17. 深入理解java虚拟机之——垃圾回收(垃圾判断,垃圾收集算法,垃圾收集器)
  18. IDEA 安装快捷键提示工具:Key promoter X
  19. 【MySQL】SQL优化
  20. 如何使用predict()输出预测结果 以及 输出值转换为0/1值。

热门文章

  1. leetcode377与leetcode518
  2. 最新Android SDK r23
  3. C4D R23-R19 安装包(winmac)安装教程及资源文件
  4. 体验 RAD Studio C++ Builder 11.1.5
  5. 新兴网络市场-房地产.(reposted)
  6. 基于51单片机的电动车防盗系统proteus仿真
  7. 此生不戒多巴胺-冲刺日志(第二天)
  8. AT+CREG AT+CREG
  9. 香蕉派M1,R1最新内核,通用版本正式发布,欢迎下载测试
  10. Cesium距离测量之思路解析加源码