《数据结构》复习之二叉树
来源:互联网 发布:flashfxp 软件下载 编辑:程序博客网 时间:2024/05/11 22:38
- 二叉树的性质
- 1满二叉树和完全二叉树
- 2二叉树的主要性质
- 二叉树的数据结构
- 二叉树的算法
- 补充
- 总结
1.二叉树的性质
1.1满二叉树和完全二叉树
在一棵二叉树中,如果所有的分支节点都有左孩子和右孩子,并且叶子节点都集中在二叉树的最下一层,则这样的二叉树被称为满二叉树。
如果一棵深度为k有n个节点的二叉树进行编号后,各结点的编号与深度为k的满二叉树中相同位置山的结点的编号军相投,那么这棵二叉树就是一颗完全二叉树。
1.2二叉树的主要性质
- 总分支数=总结点数-1(这条结论对任何树都适用,不止是二叉树)
证明:在二叉树中除根节点之外,每一个结点都有唯一的一个分支指向它,由此可证。 - 非空二叉树上叶子节点数等于双分支节点数+1
证明:由上一条性质可证。设二叉树叶子节点数为n0,单分支节点数为n1,双分支节点数为n2,则总结点数为n0+n1+n2。总分支数为2*n2+n1。由上一条性质可得,n0+n1+n2-1=2*n2+n1。化简得:n0=n2+1。(注意,这种证明方法常常被用到) - 二叉树的第i层上最多有2i-1(i>=1)个节点。
证明:等比数列。 - 高度为k的二叉树最多有2k-1(K>=1)个节点。换句话说满二叉树中前k层的结点个数为2k-1。
证明:等比数列求和问题。 有n个阶段的完全二叉树,对各节点从上到下,从左到右依次编号(编号范围1~n),则节点之间有如下关系。
若i为某节点a的编号,则:
如果i!=1,则双亲节点的编号为i/2向下取整。
如果2i<=n,则左孩子的编号为2i;如果2i>n,则a无左孩子。
如果2i+1<=n,则右孩子的编号为2i+1;如果2i+1>n,则a无右孩子。这些性质中最常用的还是1-4条性质。
2.二叉树的数据结构
二叉树也有顺序存储结构和链式存储结构。顺序存储结构是用数组存储,下标遵循上面第5条性质,注意下标从1开始。链式存储结构是最常用的存储二叉树的结构,如下图所示。
其中data表示节点数据域,用与存储对应的数据元素;lchild和rchild分别表示左指针域和右指针域,分别用于存储左孩子结点和右孩子结点的位置。
定义如下:
typedef struct BTNode{ char data; struct BTNode *lchild; struct BTNode *rchild;}BTNode
3.二叉树的算法
二叉树的算法主要是遍历算法,包括深度遍历(先序遍历,中序遍历,后序遍历)和广度遍历(层次遍历)。这也是解大多数二叉树题目的关键。
三种遍历(先序遍历,中序遍历,后序遍历)代码如下
void PreOrderTraverse(BiTree T) //先序遍历{ if(T!=NULL) { cout<<T->data<<" "; PreOrderTraverse(T->lchild); PreOrderTraverse(T->rchild); }}void InOrderTraverse(BiTree T) //中序遍历{ if(T!=NULL) { InOrderTraverse(T->lchild); cout<<T->data<<" "; InOrderTraverse(T->rchild); }}void PostOrderTraverse(BiTree T) //后序遍历{ if(T!=NULL) { PostOrderTraverse(T->lchild); PostOrderTraverse(T->rchild); cout<<T->data<<" "; }}
层次遍历的伪代码如下
根节点入队while(队不空){ 节点出队,并访问 if(左子树不为空) 左子树入队 if(右子树不为空) 右子树入队}
c++代码如下:
void LevelOrderTraverse(){ int front=0,rear=0; //定义循环队列 BiTNode *que[Maxsize]; front=rear; BiTNode *q; if(T!=NULL) //如果传过来的树不为空 { rear=(rear+1)%Maxsize; que[rear]=T; //根节点入队 while (front!=rear) //队列不为空 { front=(front+1)%Maxsize; q=que[front]; //队头出队 cout<<q->data<<" "; //访问队头 if (q->lchild!=NULL) //如果左子树不空,则左子根入队 { rear=(rear+1)%Maxsize; que[rear]=q->lchild; } if (q->rchild!=NULL) //如果右子树不空,则右子根入队 { rear=(rear+1)%Maxsize; que[rear]=q->rchild; } } }}
这4个算法(其实只算两个)就像模板一样,大多数二叉树题目只要会套这个模板就能解决,因此它非常重要。
4.补充
下面的程序是我大二数据结构课设时写的,现在看起来有许多不足和幼稚的地方,但仍然具有一定的参考价值,将它放在这里。
#define Maxsize 100#include<ctype.h>#include<string.h>#include<stdlib.h>#include<fstream>#include<string>#include<iostream>using namespace std;typedef char TElemType;typedef struct BiTNode{ TElemType data; int flag; int layer; struct BiTNode *lchild,*rchild;}BiTNode,* BiTree;#include "SqStack.h"class BinaryTreeOperator // 二叉树操作类{public: BiTree T; BinaryTreeOperator() { InitBiTree(); } ~BinaryTreeOperator() { DestroyBitree(T); } void InitBiTree(); //初始化函数 BiTree CreatBitree(char *str1,char*str2,int i,int j,int k,int l); //根据先序和后序遍历结果建立二叉树 void DestroyBitree(BiTree T); //销毁二叉树函数 void PreOrderTraverse(BiTree T); //递归先序遍历 void InOrderTraverse(BiTree T); //递归中序遍历 void PostOrderTraverse(BiTree T); //递归后序遍历 void NOPreOrder(); //非递归先序遍历 void NOInOrder(); //非递归中序遍历 void NOPostOrder(); //非递归后序遍历 void LevelOrderTraverse(); //层次遍历 void display(BiTree T); //按二叉树形态遍历输出 int BiTreeDepth(BiTree T); //求二叉树的深度 void computelayer(); //标记二叉树的层数 int lush(); void CountLeaf(BiTree T,int &num); //数叶子 void Exchange(BiTree T); //交换左右子树 void JudgeTree(); //判断一棵是否为完全二叉树};BiTree BinaryTreeOperator::CreatBitree(char *str1,char*str2,int i,int j,int k,int l) //根据先序和后序遍历结果建立二叉树{ int n; BiTNode *p; p = new BiTNode; p->data=str2[l]; n=i; for (;str1[n]!=str2[l];n++); if (n==i) { p->lchild=NULL; } else { p->lchild=CreatBitree(str1,str2,i,n-1,k,k+n-i-1); } if (n==j) { p->rchild=NULL; } else { p->rchild=CreatBitree(str1,str2,n+1,j,k+n-i,l-1); } return p;}void BinaryTreeOperator::InitBiTree() //初始化二叉树{ T = new BiTNode; if (!T) { cout<<"初始化失败!"<<endl; exit(0); } T->data=NULL; T->lchild=NULL; T->rchild=NULL;}void BinaryTreeOperator::DestroyBitree(BiTree T) //销毁二叉树{ if(T!=NULL) { DestroyBitree(T->lchild); DestroyBitree(T->rchild); delete T; }}void BinaryTreeOperator::PreOrderTraverse(BiTree T) //递归先序遍历{ if(T!=NULL) { cout<<T->data<<" "; PreOrderTraverse(T->lchild); PreOrderTraverse(T->rchild); }}void BinaryTreeOperator::InOrderTraverse(BiTree T)//递归中序遍历{ if(T!=NULL) { InOrderTraverse(T->lchild); cout<<T->data<<" "; InOrderTraverse(T->rchild); }}void BinaryTreeOperator::PostOrderTraverse(BiTree T)//递归后序遍历{ if(T!=NULL) { PostOrderTraverse(T->lchild); PostOrderTraverse(T->rchild); cout<<T->data<<" "; }}void BinaryTreeOperator::NOPreOrder() //非递归先序遍历{ if (T!=NULL) { BiTNode * p; SqStack S; InitStack(S); Push(S,T); while (!StackEmpty(S)) { Pop(S,p); cout<<p->data<<" "; if (p->rchild!=NULL) { Push(S,p->rchild); } if (p->lchild!=NULL) { Push(S,p->lchild); } } }}void BinaryTreeOperator::NOInOrder() //非递归中序遍历{ if (T!=NULL) { BiTNode * p; SqStack S; InitStack(S); Push(S,T); while (!StackEmpty(S)) { while (GetTop(S,p)&&p) { Push(S,p->lchild); } Pop(S,p); if (!StackEmpty(S)) { Pop(S,p); cout<<p->data<<" "; Push(S,p->rchild); } } }}void BinaryTreeOperator::NOPostOrder() //非递归后序遍历{ BiTNode *t; BiTNode *p; BiTNode *s; SqStack Stack; InitStack(Stack); t=T; p=T; while(t) //让其一直往左走,并入栈 { t->flag=0; //做好标志位,0表示右指数未访问,1表示右指数被访问 Push(Stack,t); t=t->lchild; } while (!StackEmpty(Stack)) { s=GetTop(Stack); if(s->rchild==NULL||s->flag==1) //如果右指数为空或标志位为1则出栈并输出 { Pop(Stack,p); cout<<p->data<<" "; } else //并对右子树做相同的操作 { s->flag=1; p=s->rchild; while (p!=NULL) { p->flag=0; Push(Stack,p); p=p->lchild; } } } //无需管入栈的,因为下一轮循环会处理好}void BinaryTreeOperator::LevelOrderTraverse() //层次遍历{ int front=0,rear=0; BiTNode *que[Maxsize]; front=rear; BiTNode *q; if(T!=NULL) //如果传过来的树不为空 { rear=(rear+1)%Maxsize; que[rear]=T; //根节点入队 while (front!=rear) //队列不为空 { front=(front+1)%Maxsize; q=que[front]; //队头出队 cout<<q->data<<" "; //访问队头 if (q->lchild!=NULL) //如果左子树不空,则左子根入队 { rear=(rear+1)%Maxsize; que[rear]=q->lchild; } if (q->rchild!=NULL) //如果右子树不空,则右子根入队 { rear=(rear+1)%Maxsize; que[rear]=q->rchild; } } }}void BinaryTreeOperator::display(BiTree T) //利用层数,横向打印{ int i; if (T==NULL) { return; } display(T->rchild); for (i=1;i<T->layer;i++) { cout<<" "; } cout<<T->data<<endl; display(T->lchild);}int BinaryTreeOperator::BiTreeDepth(BiTree T) //计算二叉树深度{ int L,R; if(T==NULL) return 0; else { L=BiTreeDepth(T->lchild); R=BiTreeDepth(T->rchild); return (L>R?L:R)+1; }}void BinaryTreeOperator::computelayer() //计算层数{ int front=0,rear=0; BiTNode *que[Maxsize]; front=rear; BiTNode *q; if(T!=NULL) //如果传过来的树不为空 { T->layer=1; rear=(rear+1)%Maxsize; que[rear]=T; //根节点入队 while (front!=rear) //队列不为空 { front=(front+1)%Maxsize; q=que[front]; //队头出队 if (q->lchild!=NULL) //如果左子树不空,则左子根入队 { rear=(rear+1)%Maxsize; que[rear]=q->lchild; que[rear]->layer=q->layer+1; //计算一下层数 } if (q->rchild!=NULL) //如果右子树不空,则右子根入队 { rear=(rear+1)%Maxsize; que[rear]=q->rchild; que[rear]->layer=q->layer+1; //计算一下层数 } } }}void BinaryTreeOperator::CountLeaf(BiTree T,int &num) //数叶子节点个数{ if(T!=NULL) { if (T->lchild==NULL&&T->lchild==NULL) { num++; } CountLeaf(T->lchild,num); CountLeaf(T->rchild,num); }}void BinaryTreeOperator::Exchange(BiTree T) //交换左右子树{ if(T!=NULL) { BiTNode * p; p=T->lchild; T->lchild=T->rchild; T->rchild=p; Exchange(T->lchild); Exchange(T->rchild); } }void BinaryTreeOperator::JudgeTree() //判断是否是完全二叉树{ int front=0,rear=0; BiTNode *que[Maxsize]; front=rear; BiTNode *q; int flag=0; if(T!=NULL) //如果传过来的树不为空 { rear=(rear+1)%Maxsize; que[rear]=T; //根节点入队 while (front!=rear) //队列不为空 { front=(front+1)%Maxsize; q=que[front]; //队头出队 if (q==NULL) { while (front!=rear) { front=(front+1)%Maxsize; //根据完全二叉树的性质,层次遍历中若出现空节点后面出现其他节点则不是完全二叉树 if (que[front]!=NULL) { flag=1; } } } else { rear=(rear+1)%Maxsize; que[rear]=q->lchild; rear=(rear+1)%Maxsize; que[rear]=q->rchild; } } } if (flag==1) { cout<<"不是完全二叉树!"<<endl; } else { cout<<"是完全二叉树!"<<endl; }}int BinaryTreeOperator::lush() //计算繁茂度{ computelayer(); //将层数标记好 int C[10]={0}; int max; int front=0,rear=0; BiTNode *que[Maxsize]; front=rear; BiTNode *q; if(T!=NULL) //如果传过来的树不为空 { rear=(rear+1)%Maxsize; que[rear]=T; //根节点入队 while (front!=rear) //队列不为空 { front=(front+1)%Maxsize; q=que[front]; //队头出队 C[q->layer]++; //入队的时候对层数进行累加 if (q->lchild!=NULL) //如果左子树不空,则左子根入队 { rear=(rear+1)%Maxsize; que[rear]=q->lchild; } if (q->rchild!=NULL) //如果右子树不空,则右子根入队 { rear=(rear+1)%Maxsize; que[rear]=q->rchild; } } } max=C[1]; for (int i=2;i<=q->layer;i++) { if (C[i]>max) { max=C[i]; } } return max*BiTreeDepth(T);}int main(){ int i=0,j=20,k=0,l=20; int choice; bool loop=true; BinaryTreeOperator Tree; char str1[81]; char str2[81]; int num=0; fstream Bitreef("Bitree.txt",ios::in); if (!Bitreef) { cout<<"打开Bitree.txt文件失败"<<endl; exit(0); } Bitreef.getline(str1,81); Bitreef.getline(str2,81); while(loop) { cout<<" ***************二叉树应用****************\n"; cout<<" * 1.建立二叉树 *\n"; cout<<" * 2.递归先序遍历 *\n"; cout<<" * 3.递归中序遍历 *\n"; cout<<" * 4.递归后序遍历 *\n"; cout<<" * 5.非递归先序遍历 *\n"; cout<<" * 6.非递归中序遍历 *\n"; cout<<" * 7.非递归后序遍历 *\n"; cout<<" * 8.层次遍历 *\n"; cout<<" * 9.按形态输出 *\n"; cout<<" * 10.二叉树的深度 *\n"; cout<<" * 11.二叉树的繁茂度 *\n"; cout<<" * 12.叶子数目 *\n"; cout<<" * 13.交换左右子树 *\n"; cout<<" * 14.判断是否为完全二叉树 *\n"; cout<<" * 15.退出程序 *\n"; cout<<" *****************************************\n"; cin>>choice; switch(choice) { case 1: Tree.T=Tree.CreatBitree(str1,str2,i,j,k,l); break; case 2: Tree.PreOrderTraverse(Tree.T); cout<<endl; break; case 3: Tree.InOrderTraverse(Tree.T); cout<<endl; break; case 4: Tree.PostOrderTraverse(Tree.T); cout<<endl; break; case 5: Tree.NOPreOrder(); cout<<endl; break; case 6: Tree.NOInOrder(); cout<<endl; break; case 7: Tree.NOPostOrder(); cout<<endl; break; case 8: Tree.LevelOrderTraverse(); cout<<endl; break; case 9: Tree.computelayer(); Tree.display(Tree.T); break; case 10: cout<<"二叉树的深度是:"<<Tree.BiTreeDepth(Tree.T)<<endl; break; case 11: cout<<"二叉树的繁茂度是:"<<Tree.lush()<<endl; break; case 12: num=0; Tree.CountLeaf(Tree.T,num); cout<<"二叉树的叶子数是:"<<num<<endl; break; case 13: Tree.Exchange(Tree.T); break; case 14: Tree.JudgeTree(); break; case 15: loop=false; break; } } return 0;}
5.总结
二叉树的题目,大多通过深搜和广搜来解决。能理解其中的思想才是关键。比如在深搜的时候,要理解什么时候往下走(递归开始进入左右子树时),什么时候往上走(递归左右子树完成后)。能动态的理解整个搜索的过程(对于二叉树图能画出遍历过程)对解题将会有很大的帮助。
- 《数据结构》复习之二叉树
- 复习数据结构之二叉查找树
- 数据结构复习之二叉树(1)
- 数据结构复习之二叉树(2)
- 数据结构复习之二叉树(3)
- 【数据结构二叉树复习】
- 【NOIp复习】数据结构之栈、队列和二叉树
- 数据结构复习之【树】
- 数据结构之【树】--复习
- 数据结构复习之【树】
- [数据结构复习]树_二叉树
- 数据结构复习(完全二叉树判定)
- 《数据结构》复习笔记--二叉树1
- 《数据结构》复习笔记--二叉树2
- 《数据结构复习笔记》--二叉搜索树
- 《数据结构复习笔记》--二叉平衡树
- 面试复习-------算法与数据结构------二叉树
- Java复习之二叉树
- 管道的限度及实现
- 在 Visual Studio 上更好开发 Python 的六大功能!
- Xcode 代码自动对齐 Xcode代码折叠 Xcode快捷键
- mybatis环境搭建
- Hive是什么?
- 《数据结构》复习之二叉树
- Fedora 22/23升级到 Fedora 24
- java web
- 论文笔记 | Network In Network
- TSP问题之模拟退火算法
- C++单链表基本操作练习
- 课程设计 --- 黑白棋中的 AI
- Reaction of travel book-HangZhou
- UDP编程中的connect