您的位置:首页 > 理论基础 > 数据结构算法

二叉树与其基本递归操作:创建、遍历、特征量计算等

2016-12-11 22:58 489 查看

一、基本概念:

1、什么是树(Tree)?

通常,我们所学的数据结构在现实生活中能找到其灵感来源,那么树,顾名思义,其结构就像一棵树,只不过这棵树有点奇怪,它是倒过来的。如下所示:



在数据结构中,关于树的名词很多,但是基本都是看见这个名词,我们就能就知道这个名词所表述的大概意思,根据上面这棵树,我们对其中重要的一些名词作以简单的解释:

(1)、根节点:树的第一个节点,它位于树的第一层,第一层有且仅有一个节点,那就是根节点(图中:1);

(2)、叶子节点:为于树的最底层,这个节点之后就不在有节点了(图中:5,6,7,8,10);

(3)、树的深度:简单来说就是这个结构的层数(该图有四层:第一层为1、第二层为2、3、4,第三层为5、6、7、8、9,第四层为10);

(4)、父节点与子节点:这是一个相对的概念,只有根节点是绝对的父亲节点,叶子节点是绝对的子节点;而相对就如节点2,它作为父节点其子节点就是5和6,而2节点作为子节点它的父节点就是1;

(5)、子树:每个可作为父节点的节点,都可以视作为一个子树的“根节点”,比如1是整个树的根节点,而节点2又可以作为2、5、6这个子树的根节点,同样节点5也是更小一级的子树,该子树根节点和叶子节点是同一个。

注意:树的每个节点只能有一个父节点(根节点除外),只有父子节点才能通过链连接起来(指针指向),也就是说,任何一个节点的指针域只能指向其子节点且不可能存在一个子节点有两个父节点。

2、什么是二叉树?

二叉树,顾名思义就是有两个叉的树,这又是一个相对的概念:每个节点,其作为父节点时最多有两个子树(左子树与右子树)。而上图中节点1有三个子树,所以不是二叉树。二叉树如下图所示(图中,每一个节点最多有两个子树):



二、二叉树的创建:

1、三种遍历方式:

首先,我们得知道树的三种遍历方式(先序遍历、中序遍历、后序遍历),才可以利用递归的方式来创建一棵树,但是不用递归的方式也能够创建二叉树,只不过比较麻烦,我们就拿递归来说,因为递归几乎存在于树的每一种操作之中。

(1)、先序遍历:所谓先序,就是指先遍历根节点,然后是左子树,最后遍历右子树。以上步骤递归执行

(2)、中序遍历:先遍历左子树,再遍历根节点,最后遍历右子树。以上步骤递归执行

(3)、后序遍历:先遍历左子树,再遍历右子树,最后遍历根节点。以上步骤递归执行

注意:三种方式中,左子树与右子树必定是左子树先遍历,先序、中序、后序指的是“根节点”的遍历先后顺序。而且其中都强调了以上步骤递归执行,关于这一点,我们举例说明:



我们以先序遍历为例,加以分析:

上图中,A是根节点,所以先遍历A,接着遍历A的左子树;遍历A的左子树时,由于B是左子树的根节点,所以先遍历B,再遍历B的左子树D,D节点遍历完后,由于D节点无左子树也无右子树,所以B的左子树遍历完成;再遍历B的右子树E(只有这一个节点),E遍历之后A的左子树就遍历完成了;在遍历A的右子树C,C又分为左子树F和G,所以先遍历C再遍历F最后遍历G。

先序遍历:ABDECFG
中序遍历:DBEAFCG
后序遍历:DEBFGCA


2、先序创建一棵树:

(1)、结构体声明:

/*二叉树的结构体声明*/
typedef struct BinaryTree{
char data;/*字符类型的数据域*/
struct BinaryTree * pLeft;/*指向左子树的指针域*/
struct BinaryTree * pRight;/*指向右子树的指针域*/
}BTREE;


(2)、先序创建:

/*依照先序遍历先序创建,测试代码在最后贴出*/
void pre_create(BTREE ** pRoot)/*main函数传递指向根节点的指针的地址*/
{
char num;
scanf("%c",&num);
if(num == '^'){/*如果输入符号为^,表示此处为叶子节点的子树(叶子节点指针为空)*/
*pRoot = NULL;/*子树的指针赋为空*/
}
else{
*pRoot = (BTREE *)malloc(sizeof(BTREE));
(*pRoot)->data = num;/*先序对根节点赋值*/
pre_create(&((*pRoot)->pLeft));/*其次递归处理左子树,传参为指向左子树的指针的地址*/
pre_create(&((*pRoot)->pRight));/*最后递归处理右子树,传参为指向右子树的指针的地址*/
}
}


三、对于二叉树的操作:

1、遍历:

(1)、先序遍历:

void pre_traversal(BTREE * pRoot)/*传参为根节点地址*/
{
if(pRoot == NULL){
return;/*如果根节点为NULL,表明已访问到叶子节点,返回上一层*/
}
else{
printf("%-4c",pRoot->data);/*先输出根节点*/
pre_traversal(pRoot->pLeft);/*再先序递归遍历左子树*/
pre_traversal(pRoot->pRight);/*最后先序递归遍历右子树*/
}
}


(2)、中序遍历与后序遍历:

其基本算法思想与先序遍历相同,只需要将先序遍历的else中的三行代码调整顺序即可:①中序:先递归中序遍历左子树,再输出根节点,最后递归中序遍历右子树;②后序:先递归中序遍历左子树,再递归中序遍历右子树,最后输出根节点。

2、计算树的深度:

int deep_tree(BTREE * pRoot)
{
if(pRoot == NULL)
return 0;/*根指针为空,为一个空树,深度为0*/
else {
int deep_left = deep_tree(pRoot->pLeft); /*递归计算左子树深度*/
int deep_right = deep_tree(pRoot->pRight); /*递归计算右子树深度*/

if(deep_left > deep_right)
return deep_left + 1;/*左子树深度大于右子树深度返回左子树深度加1*/
else
return deep_right + 1;/*否则返回右子树深度加1*/
}
}


3、计算叶子节点的个数:

/*计算叶子节点个数*/
int count_leave(BTREE * pRoot)
{
if(pRoot == NULL)
return 0;/*根节点为空返回0*/
else    if(pRoot->pLeft == NULL && pRoot->pRight == NULL)
return 1;/*根节点的左指针与右指针均指向NULL,返回1*/
else
return (count_leave(pRoot->pLeft) + count_leave(pRoot->pRight));/*左子树和右子树有任意一个不为空,则继续递归计算*/
}


四、测试用代码与测试结果:

1、测试代码:

#include<stdio.h>
#include<stdlib.h>
/*节点类型声明*/
typedef struct BinaryTree{
char data;
struct BinaryTree * pLeft;
struct BinaryTree * pRight;
}BTREE;
/*函数声明*/
void pre_create(BTREE **);
void pre_traversal(BTREE *);
void mid_traversal(BTREE *);
void after_traversal(BTREE *);
int deep_tree(BTREE *);
int count_leave(BTREE *);

int main(void)
{
BTREE * pRoot;
pre_create(&pRoot);
printf("先序遍历:");
pre_traversal(pRoot);
printf("\n中序遍历:");
mid_traversal(pRoot);
printf("\n后序遍历:");
after_traversal(pRoot);

int deep = deep_tree(pRoot);
printf("\n树的深度为:%d",deep);
int count = count_leave(pRoot);
printf("\n叶子节点个数为:%d\n",count);
return 0;
}
/*先序创建二叉树*/
void pre_create(BTREE ** pRoot)
{
char num;
scanf("%c",&num);
if(num == '^'){
*pRoot = NULL;
}
else{
*pRoot = (BTREE *)malloc(sizeof(BTREE));
(*pRoot)->data = num;
pre_create(&((*pRoot)->pLeft));
pre_create(&((*pRoot)->pRight));
}
}
/*先序遍历*/
void pre_traversal(BTREE * pRoot)
{
if(pRoot == NULL){
return;
}
else{
printf("%-4c",pRoot->data);
pre_traversal(pRoot->pLeft);
pre_traversal(pRoot->pRight);
}
}
/*中序遍历*/
void mid_traversal(BTREE * pRoot)
{
if(pRoot == NULL){
return;
}
else{
mid_traversal(pRoot->pLeft);
printf("%-4c",pRoot->data);
mid_traversal(pRoot->pRight);
}
}
/*后序遍历*/
void after_traversal(BTREE * pRoot)
{
if(pRoot == NULL){
return;
}
else{
after_traversal(pRoot->pLeft);
after_traversal(pRoot->pRight);
printf("%-4c",pRoot->data);
}
}
/*计算树的深度*/
int deep_tree(BTREE * pRoot)
{
if(pRoot == NULL)
return 0;
else {
int deep_left = deep_tree(pRoot->pLeft);
int deep_right = deep_tree(pRoot->pRight);

if(deep_left > deep_right)
return deep_left + 1;
else
return deep_right + 1;
}
}
/*计算树的叶子节点个数*/
int count_leave(BTREE * pRoot)
{
if(pRoot == NULL)
return 0;
else    if(pRoot->pLeft == NULL && pRoot->pRight == NULL)
return 1;
else
return (count_leave(pRoot->pLeft) + count_leave(pRoot->pRight));
}


2、测试与测试结果:

我们就采用下图所示树做一个简单的测试,输入的步骤(先序)如图所示,沿红色箭头反向,按顺序输入15个字符(A~F和8NULL(^)):



已知该树:
先序遍历:A、B、D、E、C、F、G
中序遍历:D、B、E、A、F、C、G
后序遍历:D、E、B、F、G、C、A
深度为:3
叶子节点个数为:4(D、E、F、G)




测试结果与计算结果相同。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐