在介绍二叉树之前,先介绍一下树的概念。
生活中常见的树都是由树根、茎干、树枝和树叶组成的。数据结构中的树把树枝分叉处、树叶、树根抽象为节点,树根抽象为根节点。对一棵树来说只有一个根节点,树叶称之为叶子节点,且叶子节点不再延伸出新的节点,把树枝和茎干抽象为边,一条边只能用来连接两个节点。在数据结构中一般把根节点置于置于最上方,然后向下延伸若干条边到达子节点。数的一些性质如下:
- 没有节点的树称之为空树;
- 树的层次从根节点开始算起,根节点为第一层,根节点的子树的根节点为第二层,以此类推;
- 节点的子树的棵数称之为节点的度,树中节点的最大的度称之为树的度;
- 树中不存在环,所以树的边数等于节点数减一;
- 叶子节点是度为零的节点,当一棵树只有一个根节点时,根节点也可以称之为叶子节点;
- 节点的深度是指从根节点(深度为1)开始,向下逐层累加至该节点时的深度值;
- 节点的高度是指从最底层的叶子节点开始开始逐层向上累加至该节点的高度值;
二叉树的递归定义:
- 要么二叉树没有根节点,是一颗空树;
- 要么二叉树由根节点,左子树、右子树组成,且左子树和右子树都是二叉树
递归定义通俗来讲:在一个家族里面,可以把爷爷说成父亲的父亲,而曾祖父可以说成父亲的父亲的父亲,这样家族里面的所有男性都可以用父亲这样的递归定义来定义了。
二叉树的每个节点的子节点的个数不超过2,同时二叉树的左子树和右子树是严格区分的,不能随意交换。
下面以数组为例,将数组中的元素建立为二叉树的节点,示例一下二叉树的基本操作,代码如下:
#include<stdio.h>
struct node //定义节点
{
int data; //数据部分
node* left; //左子树
node* right; //右子树
};
node* newNode(int data) //创建新节点
{
node* Node=new node;
Node->data=data;
Node->left=NULL; //左右子树指向的地址设置为NULL
Node->right=NULL;
return Node;
}
void insert(node* &root,int data) //插入节点
{
if(root==NULL)
{
root=newNode(data); //递归到空节点的位置就是新节点的插入位置
return;
}
if(data%2==0) // 为偶数 则插入左子树
{
insert(root->left,data);
}
else // 为奇数 则插入左子树
{
insert(root->right,data);
}
}
node* Creat(int data[],int n) //创建二叉树函数
{
node* root=NULL;
for(int i=0;i<n;i++)
{
insert(root,data[i]); //调用插入节点函数
}
return root;
}
void preorder(node *root) //先序遍历二叉树
{
if(root==NULL)
{
return;
}
printf("%d ",root->data);
preorder(root->left); //递归左子树
preorder(root->right); //递归右子树
}
int main()
{
int data[]={0,1,2,3,4,5,6,7,8,9}; //定义测试数组
node* root=NULL; //定义根节点
root=Creat(data,10);
preorder(root) ; //调用遍历函数
}
在二叉树的遍历中,除了先序遍历,还有中序遍历、后续遍历和层序遍历,都是用递归的方法。代码如下:
#include<cstdio>
#include<queue>
using namespace std;
void inorder(node* root) //中序遍历
{
if(root==NULL)
{
return;
}
inorder(root->left);
printf("%d ",root->data);
inorder(root->right);
}
void posorder(node* root) //后序遍历
{
if(root==NULL)
{
return;
}
posorder(root->left);
posorder(root->right);
printf("%d ",root->data);
}
void layerorder(node* root) //层序遍历
{
queue<node*> q; //构造一个队列
q.push(root);
while(!q.empty())
{
node* now=q.front();
q.pop();
printf("%d ",now->data);
if(now->left!=NULL)
{
q.push(now->left); //将左子树导入队列
}
if(now->right!=NULL)
{
q.push(now->right); //将右子树导入队列
}
}
}
可以看出,先序遍历、中序遍历和后续遍历的顺序指的是根节点的书序,即先序遍历的访问顺序是:根节点->左子树->右子树。
中序遍历的访问顺序是:左子树->根节点->右子树;后序遍历的访问顺序是:左子树->右子树->根节点。