本文章主要介绍二叉树的概念、特点、以及二叉树的相关操作。
1.二叉树的概念
一棵二叉树时节点的有限集合,该二叉树或者为空树、或者为只有一个根节点的树、或者为一个根节点有左右子树的树。总之,二叉树的每个节点最多有两个子树。下面画图表示二叉树的几种情况:
2. 二叉树的特点
(1)二叉树是递归定义的;
(2)二叉树的每个节点最多有两个子树即二叉树的度均不大于2;
(3)二叉树的子树有左右之分,其左右子树的次序不能颠倒。
3. 二叉树的相关操作
首先,怎么表示一棵树呢?我们可以类似于链表的形式,将一个指针变量的指向表示一棵树。然后,如何存储二叉树的节点呢?由于二叉树的特点:每个节点最多只有两个子树,故可以将二叉树每个节点的结构体设置为存放当前节点的元素,以及当前节点的左右子树的指向。依据这样的分析,写出关于二叉树的头文件tree.h如下:
//头文件只被编译一次
#pragma once
//宏定义一个标识符,用于测试时打印函数名
#define HEADER printf("================%s===============\n",__FUNCTION__)
//自定义树节点的数据类型,方便用户修改树节点的数据类型,默认设置为char类型
typedef char TreeNodeType;
//使用孩子表示法来表示树节点
typedef struct TreeNode{
//存储树节点的元素
TreeNodeType data;
//存储树节点的左子树的指向
struct TreeNode* lchild;
//存储树节点的右子树的指向
struct TreeNode* rchild;
}TreeNode;
3.1 二叉树的初始化、销毁节点、创建节点
初始化:既然是以一个指针变量的指向表示一棵二叉树,那么二叉树的初始化即将指针变量的指向置为空,初始化为一棵空二叉树。
//1.初始化
//思路:由于是使用根节点的指针表示一棵树,所以将根节点的指针置为NULL即表示初始化二叉树为空树
void TreeInit(TreeNode** proot)
{
//非法判断
if(proot==NULL)
{
return;
}
//将根节点的指针置为NULL
*proot=NULL;
}
销毁节点:销毁一个节点,相当于释放掉申请的内存空间,即使用free即可。
//2.销毁节点
//思路:直接将节点的指向释放
void DestroyTreeNode(TreeNode* node)
{
free(node);
}
创建节点:先申请一块新内存,用于存放二叉树节点信息;再将节点的data域置为传入的参数值;3.最后,由于不知道该节点的左右子树是谁,所以暂且将该节点的左右子树的指向置为空。
//3.创建节点
//思路:将元素值赋值给节点的data,并将节点的lchild和rchild置为NULL
TreeNode* CreateTreeNode(TreeNodeType value)
{
//动态申请新节点的内存
TreeNode* new_node=(TreeNode*)malloc(sizeof(TreeNode));
//给new_node的data赋值
new_node->data=value;
//给new_node的lchild和rchild置为NULL
new_node->lchild=NULL;
new_node->rchild=NULL;
//返回创建的新节点的指向
return new_node;
}
3.2 关于二叉树的遍历:先序遍历、中序遍历、后序遍历、层序遍历
1.先序遍历
遍历的顺序为根左右,具体操作分析如下:
(1)a这棵树,先访问根节点a,再访问a的左子树b(左边红色框内为a的左子树)
(2)b这棵树,先访问根节点b,再访问b的左子树d(左边绿色框内为b的左子树)
(3)d这棵树,先访问根节点d,d没有左右子树,所以根据根左右顺序,访问b这棵树的右子树e(右边绿色框内为b的右子树)
(4)e这棵树,先访访问根节点e,再访问e的左子树g(左边紫色框内为e的左子树)
(5)g这棵树,先访问根节点g,g没有左右子树,所以根据根左右顺序,访问a这棵树的右子树c(右边红色框内为a的右子树)
(6)c这棵树,先访问根节点c,c没有左子树,所以根据根左右的顺序,访问c这棵树的右子树f(右边蓝色框内为c的右子树)
(7)f这棵树,先访问根节点f,f没有左右子树,所以根据根左右顺序,整个二叉树访问完毕!
故,先