二叉树
二叉树的定义:一个二叉树是一个有穷的结点集合。这个集合可为空。若集合不为空,则它是由根结点和左子树,右子树两个二叉树组成;
数据对象集:一个有穷集合,可为空;
操作集:创建一个二叉树,判断树空,遍历;
具体实现
对于静态二叉树(就是创建之后不再变化的二叉树)可以选用顺序存储实现,就是根据数组下标的倍数关系来得出结点的父子关系,但是这样的缺点就是如果在二叉树中某个结点为空,其对应的数组位置还是要保留的,这样会造成空间的浪费。对于一般的二叉树,为了操作的方便,还是选用链表来存储。首先给出头文件和定义,方便之后的局部代码分析。数据类型以int为例,其余同理。空结点的值置为0,就是一旦输入0,表示没有这个结点。
#include<iostream>
#include<queue>
#define EMPTYNODE 0//空结点的值为0
using namespace std;
struct node
{
int data;
struct node* left;//左孩子指针
struct node* right;//右孩子指针
};
typedef struct node* N;
创建一个二叉树
这里采用层序法来创建一个二叉树,需要使用队列这种数据结构。首先将根结点入队,然后输入左孩子和右孩子,如果有意义(不为空结点)就将两个孩子入队,循环直至队列为空。
N CreateBinTree()//创建一个二叉树
{
queue<N> q;//结点指针的队列
int temp;
N roof,t;
cout<<"请输入二叉树序列:";
cin>>temp;
if(temp==EMPTYNODE)
{
cout<<"该树为空。"<<endl;
return NULL;
}
else//创建根结点
{
roof=new node;
roof->data=temp;
roof->left=NULL;
roof->right=NULL;
q.push(roof);
}
while(!q.empty())
{
t=q.front();
q.pop();
cin>>temp;//输入左孩子
if(temp!=EMPTYNODE)
{
t->left=new node;
(t->left)->data=temp;
(t->left)->left=NULL;
(t->left)->right=NULL;
q.push(t->left);
}
cin>>temp;//输入右孩子
if(temp!=EMPTYNODE)
{
t->right=new node;
(t->right)->data=temp;
(t->right)->left=NULL;
(t->right)->right=NULL;
q.push(t->right);
}
}
return roof;
}
判断树空
这个比较简单,如果根结点为空,那么树就为空。
bool IsEmpty(N n)//判断树空
{
return n;
}
遍历
遍历就是访问各个结点,且每个结点只访问一次。二叉树的遍历方式有很多,我觉得大概分为两大类。一类是以结点为主,按照访问当前结点,左子树和右子树的顺序不同又可分为先序遍历(中左右),中序遍历(左中右)和后序遍历(左右中)。我们可以从中得出规律,就是左子树一定在右子树之前,然后根据当前结点的位置就可以得到遍历的名称。一类是层序遍历,就是一层一层的访问各个结点,比较简单粗暴。
第一类遍历方式可以用递归实现,比较简单。
先序遍历
void FrontBrowse(N n)//先序遍历
{
if(n)
{
cout<<n->data<<" ";
FrontBrowse(n->left);
FrontBrowse(n->right);
}
}
中序遍历
void CenterBrowse(N n)//中序遍历
{
if(n)
{
CenterBrowse(n->left);
cout<<n->data<<" ";
CenterBrowse(n->right);
}
后序遍历
void AfterBrowse(N n)//后序遍历
{
if(n)
{
AfterBrowse(n->left);
AfterBrowse(n->right);
cout<<n->data<<" ";
}
}
虽然递归遍历的代码量比较少,但是代码执行的效率并不高。我们还可以用非递归的方法实现遍历。这里需要使用堆栈这种数据结构,就是先把根结点入栈之后,一直向左遇到结点就入栈,直到左端叶子结点,然后出栈输出,再用此方法遍历当前结点的右子树。叶子结点没有右子树,但是为了实现循环内部的统一,还是要走一遍循环,以回到叶子结点的父节点。
struct pile
{
N t;//二叉树结点的指针
struct pile* next;
};
typedef struct pile* P;
P CreateStack()//创建堆栈
{
P head;
head=new pile;
head->t=NULL;
head->next=NULL;
return head;
}
void Push(P p,N n)//入栈
{
P temp;
temp=new pile;
temp->t=n;
temp->next=p->next;
p->next=temp;
}
bool IsEmpty(P p)//判断栈空
{
return p->next==NULL;
}
N Pop(P p)//出栈
{
if(IsEmpty(p))
{
cout<<"栈已空,无法进行出栈操作。"<<endl;
return NULL;
}
P temp;
temp=p->next;
p->next=temp->next;
N n=temp->t;
delete temp;
return n;
}
void ExCenterBrowse(N n)//非递归法中序遍历
{
N temp=n;
P p;
p=CreateStack();
while(temp||!IsEmpty(p))
{
while(temp)//一直向左,直至最左端的结点
{
Push(p,temp);
temp=temp->left;
}
temp=Pop(p);
cout<<temp->data<<" ";
temp=temp->right;
}
}
层序遍历
第二类遍历也就是层序遍历,需要使用队列这种数据结构,首先把根结点入队,然后进入循环,取出队首结点,输出数据,然后通过判断该结点的左右子树是否为空决定是否入队,直至队列为空。
void StoryBrowse(N n)//层序遍历
{
if(!n)
return ;
else
{
queue<N> q;
q.push(n);
while(!q.empty())
{
N temp;
temp=q.front();
q.pop();
cout<<temp->data<<" ";
if(temp->left)
q.push(temp->left);
if(temp->right)
q.push(temp->right);
}
}
}
求二叉树的深度
二叉树的深度就是层数+1,最底层的叶子节点深度为1,向上依次递增。求二叉树深度的操作不属于二叉树的操作集,但是觉得比较有用,所以在这里给出。从深度的定义我们可以得出,二叉树的深度就是左右子树深度的最大值+1,加的就是根结点,所以可以写出求二叉树深度的递归算法。
int height(N n)//求二叉树的深度
{
if(n)
{
int h1,h2,h;
h1=height(n->left);
h2=height(n->right);
h=max(h1,h2)+1;
return h;
}
else
return 0;
}
参考文献:《数据结构(第2版)》