C++ 二叉树的基本运算及实现
(一)二叉树的定义
- 二叉树是一个有限的结点的集合,这个集合或者为空,或者由一个根节点和两棵互不相交的称为左子树和右子树的二叉树组成。
- 二叉树的表示法和树的表示法一样,有树形表示法、文氏图表示法、凹入表示法和括号表示法等。
- 满二叉树:
在一棵二叉树中,如果所有分支结点都有左孩子和右孩子结点,并且叶子结点都集中在二叉树的最下一层,这样的二叉树称为满二叉树。我们可以对满二叉树的结点进行层序编号,约定从树根为1开始,按照层数从大到小、同一层从左到右的次序进行。
特点:
①叶子结点都在最下一层。
②只有度为0和度为2的结点 - 完全二叉树:
若二叉树中最多只有最下面两层的结点的度数可以小于2,并且最下面一层的叶子结点都依次排列在该层最左边的位置上,则这样的二叉树称为完全二叉树。完全二叉树的编号方法和满二叉树相同。
特点:
①叶子结点只可能在最下面两层中出现
②对于最大层次中的叶子结点,都依次排列在该层最左边的位置上
③如果有度为1的结点,只可能有一个,且该结点只有左孩子而无右孩子
④当结点总数n为奇数时,单分支结点数n1=0;当n为偶数时,n1=1. - 满二叉树时完全二叉树的一种特例,并且完全二叉树与同高度的满二叉树的对应位置结点有同一编号。
(二)二叉树的性质
性质1:非空二叉树上的叶子结点数等于双分支结点数加1
证明:
n:结点总数
n0:二叉树上的叶子结点数
n1:单分支结点数
n2:双分支结点数
①所有结点度之和=n-1
②n=n0+n1+n2
③总分支数=n1+2n2
则n=n2+1
性质2:非空二叉树第i层上最多有2i-1个结点(i>=1)
性质3:高度为h的二叉树最多有2h-1个结点(h>=1)
性质4:具有n个(n>0)结点的完全二叉树的高度为log2(n+1)或(log2n)+1
(三)二叉树的基本运算及实现
1.定义一个结点类
#include <iostream>
using namespace std;
#define MAXSIZE 100
class Node{
public:
char data;//数据元素
Node *lChild;//指向左孩子结点
Node *rChild;//指向右孩子结点
};
2.定义一个树类
class Tree{
public:
Tree();
~Tree();
void createTree(char *str);
void dispTree();
Node *findNode(char x);//查找结点
int treeDeep();
void preOrder(); //先序遍历递归
void inOrder();//中序遍历递归
void postOrder();//后序遍历递归
int nodeCount();//计算结点数
int dispLeaf();//输出叶子结点
private:
Node *r;
void destroyTree(Node *n);
void dispNode(Node *n);
Node *findNodeRe(Node *n,char e);
int treeDeepRe(Node *n);
void preOrderRe(Node *n);
void inOrderRe(Node *n);
void postOrderRe(Node *n);
int nodeCountRe(Node *n);
int dispLeafRe(Node *n);
};
3.二叉树的初始化和销毁
Tree::Tree(){
r = NULL;//建立一棵空树
}
Tree::~Tree(){
destroyTree(r);
}
void Tree::destroyTree(Node *n){//通过递归释放所有结点空间
if(n!=NULL){
destroyTree(n->lChild);
destroyTree(n->rChild);
delete n;
}
}
4.创建二叉树
void Tree::createTree(char *str){
Node *st[MAXSIZE];//建立顺序栈
Node *p;
int top=-1,k=0,j=0;
char ch;
while(str[j]!='\0'){
ch=str[j];
switch(ch){
case '(': //新建结点有孩子,将该结点进栈
top++;
st[top] = p;
k=1;
break;
case ')':
top--;
break;
case ',': //处理右孩子结点
k=2;
break;
default:
p = new Node(); //新建一个结点
p->lChild = p->rChild = NULL;
p->data = ch;
if(r==NULL){//如果还没有建立根结点,就把*p作为根结点
r = p;
}else{
switch(k){//通过k值来判断是左孩子结点还是右孩子结点
case 1:
st[top]->lChild = p;
//新建结点作为栈顶结点的左孩子结点
break;
case 2:
st[top]->rChild = p;
//新建结点作为栈顶结点的右孩子结点
break;
}
}
break;
}
j++;
}
}
5.输出二叉树
void Tree::dispTree(){//将二叉链转化成括号表示法
dispNode(this->r);
}
void Tree::dispNode(Node *n){//通过递归来输出
if(n!=NULL){
cout << n->data;
if(n->lChild!=NULL || n->rChild!=NULL){
cout << "(";
dispNode(n->lChild);//递归输出左子树
if(n->rChild!=NULL){
cout << ",";
}
dispNode(n->rChild);//递归输出右子树
cout << ")";
}
}
}
6.查找结点
Node* Tree::findNode(char x){
return findNodeRe(r,x);
}
Node* Tree::findNodeRe(Node *n,char e){//通过递归来查找
Node *p;
if(n==NULL){
return n;
}else if(n->data==e){
return n;
}else{
p = findNodeRe(n->lChild,e);//在左子树中查找
if(p!=NULL){
return p;
}else{
return findNodeRe(n->rChild,e);//在右子树中查找
}
}
}
7.求树的深度
int Tree::treeDeep(){
return treeDeepRe(r);
}
int Tree::treeDeepRe(Node *n){//通过递归求深度
int lchildh,rchildh;
if(n==NULL){//空树高度为0
return 0;
}else{
lchildh = treeDeepRe(n->lChild);//左子树高度
rchildh = treeDeepRe(n->rChild);//右子树高度
return (lchildh>rchildh)?(lchildh+1):(rchildh+1);
//返回更大的结果,因为要加上当前结点所在层数,所以加1
}
}
8.先序遍历
void Tree::preOrder(){
preOrderRe(this->r);
}
void Tree::preOrderRe(Node *n){//通过递归来先序遍历
if(n!=NULL){
cout << n->data << " ";//访问根结点
preOrderRe(n->lChild);//先序遍历左子树
preOrderRe(n->rChild);//先序遍历右子树
}
}
9.中序遍历
void Tree::inOrder(){
inOrderRe(this->r);
}
void Tree::inOrderRe(Node *n){//通过递归来中序遍历
if(n!=NULL){
preOrderRe(n->lChild);//中序遍历左子树
cout << n->data << " ";//访问根结点
preOrderRe(n->rChild);//中序遍历右子树
}
}
10.后序遍历
void Tree::postOrder(){
postOrderRe(this->r);
}
void Tree::postOrderRe(Node *n){//通过递归来后序遍历
if(n!=NULL){
preOrderRe(n->lChild);//后序遍历左子树
preOrderRe(n->rChild);//后序遍历右子树
cout << n->data << " ";//访问根结点
}
}
11.计算结点数
int Tree::nodeCount(){
nodeCountRe(this->r);
}
int Tree::nodeCountRe(Node *b){//通过递归来计算结点
int m,n,k;
if(b!=NULL){
k=1;//根结点计数为1
m=nodeCountRe(b->lChild);//遍历左子树的结点个数
n=nodeCountRe(b->rChild);//遍历右子树的结点个数
return k+m+n;
}else{
return 0;//空树结点为0
}
}
12.输出叶子结点并返回叶子结点数
int Tree::dispLeaf(){
return dispLeafRe(this->r);
}
int Tree::dispLeafRe(Node *n){//通过递归来输出叶子结点
int static num;
if(n!=NULL){
if(n->lChild==NULL&&n->rChild==NULL){//根结点为叶子结点时输出
cout << n->data << " ";
num++;
}
dispLeafRe(n->lChild);//输出左子树的叶子结点
dispLeafRe(n->rChild);//输出右子树的叶子结点
}
return num;//返回叶子结点数
}
13.main函数
int main(){
Tree t;
char c[100] = "A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))";
t.createTree(c);
cout << "该二叉树b为:" ;
t.dispTree();
cout << endl;
cout << "结点H的左孩子值为:" << t.findNode('H')->lChild->data << endl;
cout << "结点H的右孩子值为:" << t.findNode('H')->rChild->data << endl;
cout << "二叉树b的深度为:" << t.treeDeep() << endl;
cout << "叶子节点有:" ;
int num = t.dispLeaf();
cout << endl;
cout << "叶子结点个数为:" << num << endl;
cout << "先序遍历结果为:" ;
t.preOrder();
cout << endl;
cout << "中序遍历结果为:" ;
t.inOrder();
cout << endl;
cout << "后序遍历结果为:" ;
t.postOrder();
cout << endl;
return 0;
}