其他类似的博客
哈工大数据结构实验二——哈夫曼编码与译码方法
哈工大2019秋数据结构期末试题
哈工大数据结构实验一算术表达式求值
哈工大数据结构实验一 线性结构及其应用
二叉树的存储相信不用多讲啥,直接使用最常见的动态二叉链结构存储即可。
typedef struct BTnode *Tnode; //使用链式结构表示二叉树,
struct BTnode{//声明一个树的节点,
//它包含这个节点的数据以及指向左儿子和右儿子的指针
char data;//节点存储的字符
Tnode lchild,rchild; //指向左右儿子的指针
} ;
递归创建二叉树,我直接先序递归创建二叉树。和先序遍历思想几乎一样。先创建根节点,然后先序递归的创建左子树,然后先序递归的创建右子树。
我用’#'表示一个空节点
void PreCreatTree(Tnode *T){ //先序创建一个二叉树
char x;
cin>>x;
if(x=='#'){//'#'表示空节点
*T=NULL;
}
else{
*T=new BTnode;//给节点分配存储空间
if(!*T){
cout<<"error"<<endl;
}
else{
//先序递归创建二叉树
(*T)->data =x;//创建根节点
PreCreatTree(&(*T)->lchild );//先序递归的创建左子树
PreCreatTree(&(*T)->rchild );//先序递归的创建右子树
}
}
}
怎么非递归的创建二叉树呢?我们可以用数组来存储一个二叉树,从数组下标1的位置存储根节点。数组的下标就隐含描述了节点的父子关系。
那么我们的思路就明了了,每次只要给出这个节点存储的下标,那么根据上述的关系式,我们可以知道这个节点的父节点下标,也能知道这个节点是父节点的左儿子还是右儿子,然后让根节点指向这个儿子节点即可。
Tnode s1[100]; //用来创建递归树所用的数组
Tnode CreatTree(){ //非递归创建二叉树
int i,j;
char c;
Tnode bt,p;//用于存放节点
cin>>i>>c;//给定存储某个字符的数组下标和该字符来创建二叉树
while(i!=0&&c!='#') {
p=new BTnode;
p->data =c;
p->lchild =NULL;
p->rchild =NULL;
s1[i]=p;
if(i==1) bt=p;//根节点
else if(i==2) bt->lchild =p;
else{
j=i/2;//父节点
if(i%2==0) {//这个节点是父节点的左儿子
s1[j]->lchild=p;
}
if(i%2!=0)//这个节点是父节点的右儿子
s1[j]->rchild=p;
}
cin>>i>>c;
}
return bt;
}
Tnode DuRuTree(){ //非递归创建二叉树
fstream in;
in.open("erchashu.txt");
int i,j;
char c;
Tnode bt,p;//用于存放节点
in>>i>>c;
while(i!=0&&c!='#') {
p=new BTnode;
p->data =c;
p->lchild =NULL;
p->rchild =NULL;
s1[i]=p;
if(i==1) bt=p;
else if(i==2) bt->lchild =p;
else{
j=i/2;
if(i%2==0){
s1[j]->lchild=p;
}
if(i%2!=0) s1[j]->rchild=p;
}
in>>i>>c;
}
in.close() ;
return bt;
}
太简单了,不阐述了
void PreOrder(Tnode T){ //先序递归遍历二叉树
if(T==NULL){
return ;
}
cout<<T->data<<" ";
PreOrder(T->lchild );
PreOrder(T->rchild );
}
非递归先序遍历我们需要借助栈来完成,我们对先序的理解就是一直往左儿子走,沿途输出遇到的所有左儿子,直到走到最左的儿子,然后我们再去先序遍历这个左儿子的兄弟
那么问题来了,你走到了最左儿子,怎么找到左儿子的兄弟呢?由于节点不存储指向兄弟的指针,因此,你必须通过父节点去找右儿子。
现在的问题就是你怎么去找父节点呢?由于节点不存储指向父亲的指针,因此你必须把父节点保留起来。没错,就是用栈。
栈有个特性:后进先出,先进后出,也就是说栈中相邻的两个节点就是父子关系。所以当我们碰到最左节点后,栈顶的那个节点就是我们这个最左节点的父亲,只要把这个节点弹出来,然后我们就可以去继续遍历父节点的右儿子啦。(这个时候不需要输出父节点,因为父节点在进栈的时候已经输出了)
void fPreOrder(Tnode root){ //非递归先序遍历二叉树
if(root==NULL){
return;
}
Tnode p=root;
stack<Tnode>s;
while(!s.empty()||p){
while(p){ //一直往左走 , 先序遍历
cout<<p->data<<' ' ;
s.push(p);
p=p->lchild ;
}
//已经走到最左儿子
if(!s.empty() ){
p=s.top() ;//弹出父节点
s.pop() ;
p=p->rchild ; //然后去父节点的右儿子先序遍历
}
}
}
非递归中序遍历和非递归先序遍历原理一样,只是输出节点的时间不同,先序遍历是把节点压栈的时候就输出节点,非递归中序遍历是把节点从栈中弹出的时候输出节点。
void fInOrder(Tnode root){ //非递归中序遍历二叉树
if(root==NULL)
return ;
Tnode p=root;
stack<Tnode>s;
while(!s.empty()||p){
while(p){
s.push(p);
p=p->lchild ;
}
if(!s.empty()){
p=s.top();
s.pop();
cout<<p->data<<' ' ;
p=p->rchild ;
}
}
}
void InOrder(Tnode T){
if(T==NULL){
return ;
}
InOrder(T->lchild );
cout<<T->data<<" ";
InOrder(T->rchild );
}
二叉树的后序遍历是遍历中最难的,但是跟着我,你绝对能懂。
二叉树的后序遍历的难点无非在于何时输出节点。
使用一个变量plastvisit记录上一次访问的节点,这个辅助节点有什么用处?
void fPostOrder(Tnode root){ //非递归后序遍历二叉树
if(root==NULL){
return;
}
stack<Tnode>s;
Tnode pcur,plastvisit;//pcur是当前节点,plastxisit是上次访问的节点
pcur=root;
plastvisit=NULL;
while(pcur){
s.push(pcur);
pcur=pcur->lchild ;
}
while(!s.empty() ){
pcur=s.top() ;
s.pop() ;
if(pcur->rchild ==NULL||pcur->rchild ==plastvisit){
cout<<pcur->data<<' ' ;
plastvisit=pcur;
}
else{
s.push(pcur);
pcur=pcur->rchild ;
while(pcur){
s.push(pcur);
pcur=pcur->lchild ;
}
}
}
}
层序遍历很简单,我们需要借助队列
void CengOrder(Tnode T){ //层序遍历二叉树
if(!T)
return;
else{
Tnode p=T;
Tnode q;
queue<Tnode>s;
s.push(p);
while(!s.empty()){
q=s.front();
s.pop();
cout<<q->data<<' ';
if(q->lchild ) s.push(q->lchild );
if(q->rchild ) s.push(q->rchild );
}
}
}
完全二叉树:通俗的讲就是对于k层的二叉树来说,k-1层为满二叉树,k层所有叶子结点左边靠齐。
我们借助队列来进行层序遍历,遍历的时候考虑以下情况
代码如下
bool panduanwanquan(Tnode root){//判断二叉树是否为完全二叉树
if (root) {
queue<BTnode*>que;
que.push(root);
bool isLeaf = false;
while (que.size()) {
BTnode* p = que.front();
que.pop();
//如果左子树不存在 右子树存在 返回false
if (!p->lchild && p->rchild) {
return false;
}
//左子树不为空 而右子树为空 说明之后节点都为叶子节点
if (p->lchild && !p->rchild) {
//若isLeaf状态已经开启 而该节点不是叶子节点 返回false
if (isLeaf) {
return false;
}
isLeaf = true;
que.push(p->lchild);
}
//左右子树都为空 说明之后节点都为叶子节点
else if (!p->lchild && !p->rchild) {
isLeaf = true;
}
//左右子树都不空
else {
que.push(p->lchild);
que.push(p->rchild);
}
}
return true;
}
return false;
}
我采取显示二叉树的策略是,对于树根节点p ,以及左子树q和右子树k,我的显示为(p(q,k))。空节点用’#'显示。
void xianshi(Tnode T){ //显示二叉树
if(!T) cout<<"#";
else{
cout<<"(";
cout<<T->data ;
xianshi(T->lchild );
xianshi(T->rchild );
cout<<")";
}
}
二叉树的宽度是指某一层最多的节点的个数。对于处理某一层的事件来说,我们一般都会借助队列来实现。
求二叉树的宽度的方法和层序遍历二叉树的方法很相似。
思路如下:
int fwidth(Tnode T){ //非递归求二叉树的宽度
if(T==NULL){
return 0;
}
int max=1;
queue<Tnode>s;
s.push(T);
while(true){
int len=s.size() ;
if(len==0) break;
while(len>0){
Tnode t=s.front() ;
s.pop() ;
len--;
if(t->lchild )
s.push(t->lchild );
if(t->rchild )
s.push(t->rchild );
}
max=max>s.size() ? max:s.size();
}
return max;
}
#include
#include
#include
#include
using namespace std;
typedef struct BTnode *Tnode; //使用链式结构表示二叉树,
struct BTnode{ //声明一个树的节点,它包含这个节点的数据以及指向左儿子和右儿子的指针
char data;
Tnode lchild,rchild; //指向左右儿子的指针
} ;
Tnode s1[100]; //用来创建递归树所用的数组
Tnode CreatTree(){ //非递归创建二叉树
int i,j;
char c;
Tnode bt,p;//用于存放节点
cin>>i>>c;
while(i!=0&&c!='#') {
p=new BTnode;
p->data =c;
p->lchild =NULL;
p->rchild =NULL;
s1[i]=p;
if(i==1) bt=p;
else if(i==2) bt->lchild =p;
else{
j=i/2;
if(i%2==0) {
s1[j]->lchild=p;
}
if(i%2!=0)
s1[j]->rchild=p;
}
cin>>i>>c;
}
return bt;
}
void PreCreatTree(Tnode *T){ //先序创建一个二叉树
char x;
cin>>x;
if(x=='#'){//'#'表示空节点
*T=NULL;
}
else{
*T=new BTnode;//给节点分配存储空间
if(!*T){
cout<<"error"<<endl;
}
else{
//先序递归创建二叉树
(*T)->data =x;
PreCreatTree(&(*T)->lchild );
PreCreatTree(&(*T)->rchild );
}
}
}
Tnode DuRuTree(){ //非递归创建二叉树
fstream in;
in.open("erchashu.txt");
int i,j;
char c;
Tnode bt,p;//用于存放节点
in>>i>>c;
while(i!=0&&c!='#') {
p=new BTnode;
p->data =c;
p->lchild =NULL;
p->rchild =NULL;
s1[i]=p;
if(i==1) bt=p;
else if(i==2) bt->lchild =p;
else{
j=i/2;
if(i%2==0){
s1[j]->lchild=p;
}
if(i%2!=0) s1[j]->rchild=p;
}
in>>i>>c;
}
in.close() ;
return bt;
}
void PreOrder(Tnode T){ //先序递归遍历二叉树
if(T==NULL){
return ;
}
cout<<T->data<<" ";
PreOrder(T->lchild );
PreOrder(T->rchild );
}
void InOrder(Tnode T){ //中序递归遍历二叉树
if(T==NULL){
return ;
}
InOrder(T->lchild );
cout<<T->data<<" ";
InOrder(T->rchild );
}
void PostOrder(Tnode T){ //后序递归遍历二叉树
if(T==NULL){
return ;
}
PostOrder(T->lchild );
PostOrder(T->rchild );
cout<<T->data<<" ";
}
void fPreOrder(Tnode root){ //非递归先序遍历二叉树
if(root==NULL){
return;
}
Tnode p=root;
stack<Tnode>s;
while(!s.empty()||p){
while(p){
cout<<p->data<<' ' ;
s.push(p);
p=p->lchild ;
}
if(!s.empty() ){
p=s.top() ;
s.pop() ;
p=p->rchild ;
}
}
}
void fInOrder(Tnode root){ //非递归中序遍历二叉树
if(root==NULL)
return ;
Tnode p=root;
stack<Tnode>s;
while(!s.empty()||p){
while(p){
s.push(p);
p=p->lchild ;
}
if(!s.empty()){
p=s.top();
s.pop();
cout<<p->data<<' ' ;
p=p->rchild ;
}
}
}
void fPostOrder(Tnode root){ //非递归后序遍历二叉树
if(root==NULL){
return;
}
stack<Tnode>s;
Tnode pcur,plastvisit;//pcur是当前节点,plastxisit是上次访问的节点
pcur=root;
plastvisit=NULL;
while(pcur){
s.push(pcur);
pcur=pcur->lchild ;
}
while(!s.empty() ){
pcur=s.top() ;
s.pop() ;
if(pcur->rchild ==NULL||pcur->rchild ==plastvisit){
cout<<pcur->data<<' ' ;
plastvisit=pcur;
}
else{
s.push(pcur);
pcur=pcur->rchild ;
while(pcur){
s.push(pcur);
pcur=pcur->lchild ;
}
}
}
}
void CengOrder(Tnode T){ //层序遍历二叉树
if(!T)
return;
else{
Tnode p=T;
Tnode q;
queue<Tnode>s;
s.push(p);
while(!s.empty()){
q=s.front();
s.pop();
cout<<q->data<<' ';
if(q->lchild ) s.push(q->lchild );
if(q->rchild ) s.push(q->rchild );
}
}
}
bool panduanwanquan(Tnode root){//判断二叉树是否为完全二叉树
if (root) {
queue<BTnode*>que;
que.push(root);
bool isLeaf = false;
while (que.size()) {
BTnode* p = que.front();
que.pop();
//如果左子树不存在 右子树存在 返回false
if (!p->lchild && p->rchild) {
return false;
}
//左子树不为空 而右子树为空 说明之后节点都为叶子节点
if (p->lchild && !p->rchild) {
//若isLeaf状态已经开启 而该节点不是叶子节点 返回false
if (isLeaf) {
return false;
}
isLeaf = true;
que.push(p->lchild);
}
//左右子树都为空 说明之后节点都为叶子节点
else if (!p->lchild && !p->rchild) {
isLeaf = true;
}
//左右子树都不空
else {
que.push(p->lchild);
que.push(p->rchild);
}
}
return true;
}
return false;
}
int fwidth(Tnode T){ //非递归求二叉树的宽度
if(T==NULL){
return 0;
}
int max=1;
queue<Tnode>s;
s.push(T);
while(true){
int len=s.size() ;
if(len==0) break;
while(len>0){
Tnode t=s.front() ;
s.pop() ;
len--;
if(t->lchild )
s.push(t->lchild );
if(t->rchild )
s.push(t->rchild );
}
max=max>s.size() ? max:s.size();
}
return max;
}
void xianshi(Tnode T){ //显示二叉树
if(!T) cout<<"#";
else{
cout<<"(";
cout<<T->data ;
xianshi(T->lchild );
xianshi(T->rchild );
cout<<")";
}
}
void zrh(){ //控制端函数
int n;
Tnode t;
t=DuRuTree();
cout<<"当前从文件中读入的二叉树为:"<<endl;
xianshi(t);//显示当前从文件读入的二叉树
cout<<endl;
cout<<"0.退出"<<endl;
cout<<"1.显示二叉树"<<endl; //实现
cout<<"2.先序递归建立二叉树"<<endl;//实现
cout<<"3.非递归建立二叉树"<<endl;//实现
cout<<"4.先序递归遍历二叉树"<<endl;//实现
cout<<"5.中序递归遍历二叉树"<<endl;//实现
cout<<"6.后序递归遍历二叉树"<<endl;//实现
cout<<"7.先序非递归遍历二叉树"<<endl;//实现
cout<<"8.中序非递归遍历二叉树"<<endl;//实现
cout<<"9.后序非递归遍历二叉树"<<endl;//实现
cout<<"10.层序遍历二叉树" <<endl;//实现
cout<<"11.判断二叉树是否为完全树"<<endl;//实现
cout<<"12.求二叉树的宽度"<<endl; //实现
cout<<"13.从文件读入并显示二叉树"<<endl; //实现
cin>>n;
while(n!=0) {
switch(n){
case 1:
cout<<"当前二叉树为:";
xianshi(t);//显示二叉树
cout<<endl;
break;
case 2:
cout<<"2.先序递归建立二叉树(空节点用'#'表示)"<<endl;//实现
PreCreatTree(&t);
break;
case 3:
cout<<"3.非递归建立二叉树(依次输入节点的标号和内容,输入0或#结束):"<<endl;
t=CreatTree();
break;
case 4:
cout<<"当前二叉树为:";xianshi(t);cout<<endl;
cout<<"4.先序递归遍历二叉树的结果为:"<<endl;
PreOrder(t);
cout<<endl;
break;
case 5:
cout<<"当前二叉树为:";xianshi(t);cout<<endl;
cout<<"5.中序递归遍历二叉树的结果为:"<<endl;
InOrder(t);
cout<<endl;
break;
case 6:
cout<<"当前二叉树为:";xianshi(t);cout<<endl;
cout<<"6.后序递归遍历二叉树的结果为:"<<endl;
PostOrder(t);
cout<<endl;
break;
case 7:
cout<<"当前二叉树为:";xianshi(t);cout<<endl;
cout<<"7.先序非递归遍历二叉树的结果为:"<<endl;
fPreOrder(t);
cout<<endl;
break;
case 8:
cout<<"当前二叉树为:";xianshi(t);cout<<endl;
cout<<"8.中序递归遍历二叉树的结果为:"<<endl;
fInOrder(t);
cout<<endl;
break;
case 9:
cout<<"当前二叉树为:";xianshi(t);cout<<endl;
cout<<"9.后序递归遍历二叉树的结果为:"<<endl;
fPostOrder(t);
cout<<endl;
break;
case 10:
cout<<"当前二叉树为:";xianshi(t);cout<<endl;
cout<<"10.层序遍历二叉树的结果为:"<<endl;
CengOrder(t);
cout<<endl;
break;
case 11:
cout<<"当前二叉树为:";xianshi(t);cout<<endl;
cout<<"当前二叉树为完全二叉树?(1:是,2:不是):"<<panduanwanquan(t);
cout<<endl;
break;
case 12:
cout<<"当前二叉树为:";xianshi(t);cout<<endl;
cout<<"当前二叉树的宽度为:"<<fwidth(t);
cout<<endl;
break;
case 13:
cout<<"13.当前从文件读入的二叉树为:"<<endl;
t=DuRuTree();
xianshi(t);
cout<<endl;
break;
case 0:
n=0;
break;
default:
cout<<"请输入0-13的数据:"<<endl;
break;
}
if(n==0) break;
cout<<"0.退出"<<endl;
cout<<"1.显示二叉树"<<endl;
cout<<"2.先序递归建立二叉树"<<endl;
cout<<"3.非递归建立二叉树"<<endl;
cout<<"4.先序递归遍历二叉树"<<endl;
cout<<"5.中序递归遍历二叉树"<<endl;
cout<<"6.后序递归遍历二叉树"<<endl;
cout<<"7.先序非递归遍历二叉树"<<endl;
cout<<"8.中序非递归遍历二叉树"<<endl;
cout<<"9.后序非递归遍历二叉树"<<endl;
cout<<"10.层序遍历二叉树" <<endl;
cout<<"11.判断二叉树是否为完全树"<<endl;
cout<<"12.求二叉树的宽度"<<endl;
cout<<"13.从文件读入二叉树"<<endl;
cin>>n;
}
}
int main(){
zrh();//调用控制端函数
}