数据结构学习笔记——由遍历恢复二叉树以及非递归遍历二叉树

简介: 数据结构学习笔记——由遍历恢复二叉树以及非递归遍历二叉树

一、由遍历恢复二叉树


(一)由先序遍历和中序遍历


1、二叉树的先序遍历中,首先是根结点,遍历完根结点的左子树,然后再遍历完根结点的右子树,依次下去至所有结点都遍历到;

2、二叉树的中序遍历中,首先是遍历完根结点的左子树,然后是根结点,最后遍历完根结点的右子树,依次下去至所有结点都遍历到。


由于先序遍历首先是根结点,所以可以根据先序遍历确定所求二叉树的根结点,然后再通过中序遍历来确定左、右子树,其思路也是分别寻找左子树和右子树的根结点,并将左、右子树的根结点连在双亲结点后,依次进行下去。

例如,已知先序序列为ABDECFG,中序序列为DBEAFCG,由先序遍历和中序遍历恢复二叉树。

从先序序列确定二叉树的根结点:A,如下:

1667293084942.jpg

通过中序遍历来确定左、右子树,其中结点A之前的所有结点都是根结点左子树结点,其后都是根结点右子树结点,如下:

1667293099641.jpg

然后继续对先序序列和中序序列中的左、右子树进行相应进一步的分解:

1667293107046.jpg

可得到如下二叉树:

1667293114174.jpg


#include <stdio.h>
#include <malloc.h>
#define MAXSIZE 100
int front=0,rear=0;
/*1、二叉树的定义*/
typedef struct BNode {
  int data;  //数据域
  struct BNode *lchild,*rchild;  //左孩子、右孩子指针
} *BTree,BNode;
/*2、二叉树的建立*/
BTree CreateTree() {
  BTree T;
  char ch;
  scanf("%c",&ch);
  getchar();  //getchar()用于接收每次输入字符结点后的回车符,从而以便输入下一个字符结点
  if(ch=='0') //当为0时,将结点置空
  T=NULL;
  else {
  T=(BTree)malloc(sizeof(BTree)); //分配一个新的结点
  T->data=ch;
  printf("请输入%c结点的左孩子结点:",T->data);
  T->lchild=CreateTree();  //通过递归建立左孩子结点
  printf("请输入%c结点的右孩子结点:",T->data);
  T->rchild=CreateTree();  //通过递归建立右孩子结点
  }
  return T;
}
/*3、广义表输出二叉树*/
void ShowTree(BTree T) {
  if(T!=NULL) {
  //当二叉树不为空时
  printf("%c",T->data); //输入出该结点的数据域
  if(T->lchild!=NULL) {  //若该结点的左子树不为空
    printf("(");  //输出一个左括号
    ShowTree(T->lchild);  //通过递归继续输出结点的左子树结点下的各结点
    if(T->rchild!=NULL) { //若该结点右子树不为空
    printf(",");  //输出一个逗号
    ShowTree(T->rchild);  //通过递归继续输出结点的右子树结点下的各结点
    }
    printf(")");  //输出一个右括号
  } else {  //若左子树为空,右子树不为空
    if(T->rchild!=NULL) {
    printf("(");  //输出一个左括号
    ShowTree(T->lchild);  //通过递归继续输出结点的左子树结点下的各结点
    if(T->rchild!=NULL) {  //若该结点的右子树不为空
      printf(",");  //输出一个逗号
      ShowTree(T->rchild);  //通过递归继续输出结点的右子树结点下的各结点
    }
    printf(")");  //输出一个右括号
    }
  }
  }
}
/*4、先序遍历二叉树*/
bool ProTree(BTree T) {
  if(T==NULL)
  return false;   //递归结束
  else {
  printf("%c ",T->data);  //输出当前结点的数据域
  ProTree(T->lchild);  //递归继续遍历该结点的左子树
  ProTree(T->rchild);  //递归继续遍历该结点的右子树
  return true;
  }
}
/*5、中序遍历二叉树*/
bool InTree(BTree T) {
  if(T==NULL)
  return false;   //递归结束
  else {
  InTree(T->lchild);  //递归继续遍历该结点的左子树
  printf("%c ",T->data);  //输出当前结点的数据域
  InTree(T->rchild);  //递归继续遍历该结点的右子树
  return true;
  }
}
/*6、后序遍历二叉树*/
bool PostTree(BTree T) {
  if(T==NULL)
  return false;    //递归结束
  else {
  PostTree(T->lchild);  //递归继续遍历该结点的左子树
  PostTree(T->rchild);  //递归继续遍历该结点的右子树
  printf("%c ",T->data);  //输出当前结点的数据域
  return true;
  }
}
/*主函数*/
int main() {
  BTree T;
  T=NULL;
  printf("请输入二叉树的根结点:");
  T=CreateTree();  //建立二叉树
  printf("建立的二叉树如下:\n");
  ShowTree(T);  //通过广义表显示二叉树
  printf("\n");
  printf("先序:\n"); 
  ProTree(T);
  printf("\n");
  printf("中序:\n"); 
  InTree(T);
  printf("\n");
  printf("后序:\n"); 
  PostTree(T);
}


通过代码验证如下,正确:

1667293146592.jpg


(二)由中序遍历和后序遍历


1、二叉树的中序遍历中,首先是遍历完根结点的左子树,然后是根结点,最后遍历完根结点的右子树,依次下去至所有结点都遍历到;

2、二叉树的后序遍历中,首先是遍历完根结点的左子树,然后遍历完根结点的右子树,最后是根结点,依次下去至所有结点都遍历到,也就是从二叉树的底层往上层依次遍历。


根据后序遍历,找到最后一个结点为根结点,然后由中序遍历确定左、右子树,然后再通过中序遍历来确定左、右子树,其思路也是分别寻找左子树和右子树的根结点,并将左、右子树的根结点连在双亲结点后,依次进行下去。

例如,已知中序序列为DBEAFCG,后序序列为DEBFGCA,由先序遍历和中序遍历恢复二叉树。

从后序序列的最后一个结点确定二叉树的根结点:A,如下:

1667293169289.jpg

通过中序遍历来确定左、右子树,其中结点A之前的所有结点都是根结点左子树结点,其后都是根结点右子树结点,如下:

1667293179647.jpg

然后继续对中序序列和后序序列中的左、右子树进行相应进一步的分解:

1667293191425.jpg

可得到如下二叉树:

1667293198834.jpg


✨结论:由先序序列和中序序列或后序序列和中序序列或层次序列和中序序列可以唯一确定一棵二叉树,但不能由先序序列和后序序列唯一确定一棵二叉树。

例、已知某二叉树的后序遍历序列为DABEC,中序遍历序列为DEBAC,求其先序遍历序列。


由后序遍历序列可知C为二叉树的根结点,然后在中序遍历序列中,C结点之前为根结点的左子树,由于C后面无结点,则说明C结点只有左子树而无右子树:

1667293210776.jpg

然后再对后序遍历序列DABE和中序遍历序列DEAB,

1667293222331.jpg

此时还未确定结点A、B,根据后序遍历序列AB,可知B为根结点,在中序遍历序列中,结点A在B的右子树,所以二叉树如下:

1667293233901.jpg

由上图所恢复的二叉树可得,先序遍历序列为CEDBA。

通过代码验证,正确:

1667293242994.jpg


二、非递归先、中、后序遍历二叉树


之前,在先序、中序、后序遍历二叉树中,都采用了递归算法,由于递归算法中的每一次函数调用都会在内存栈中分配空间,且要做保护现场和恢复现场等一系列操作,会产生溢出,且很低效会影响效率,所以我们可以对遍历二叉树进行进一步改进,即通过非递归的算法来完成,其思路是不使用系统内部的栈来完成遍历,而是使用用户自定义的栈算法来代替,从而提升效率。


系统栈处理记录访问过的结点信息之外,还有其他信息需要记录,以实现函数的递归调用,而用户自定义的栈仅保存了遍历所需的结点信息。


例如以下图二叉树为例:

1667293255373.jpg


(一)先序遍历二叉树的非递归算法


二叉树的先序遍历(DLR)

二叉树的先序遍历中,首先是根结点,遍历完根结点的左子树,然后再遍历完根结点的右子树,依次下去至所有结点都遍历到。


分析一下具体的实现步骤:

由于我们要自己定义一个栈,初始时,栈为空。首先,结点A入栈,然后结点A再出栈,输出遍历序列为A;然后将输出结点A的左、右孩子结点入栈,由于二叉树的性质,先排左子树,所以对左孩子的遍历优先级大于右孩子,将结点A的左、右孩子结点(左孩子B和右孩子C)入栈,其中右孩子C先入栈,左孩子B再入栈,因为后入栈的结点会先出栈(栈的性质)被遍历,当前栈内序列由下至上为CB,此时结点B出栈,输出遍历序列为AB,此时将结点B的左、右孩子结点入栈(左孩子D和右孩子E);继续执行出栈,先将右孩子E先入栈,左孩子D再入栈,当前栈内序列由下至上为CED,此时结点D出栈,输出遍历序列为ABD,由于D结点无左、右孩子结点,然后再看结点E,也无,结点E出栈,输出遍历序列为ABDE;回到结点C,结点C出栈,输出遍历序列为ABDEC;然后将输出结点C的左、右孩子结点入栈,先将右孩子G入栈,然后再是左孩子F,当前栈内序列由下至上为GF,此时结点F出栈,输出遍历序列为ABDECF,由于F结点无左、右孩子结点,然后再看结点G,也无,结点G出栈,最终栈为空,输出遍历序列为ABDECFG。

1667293270308.jpg

先序遍历二叉树的非递归算法代码如下:

/*先序遍历二叉树【非递归】*/
void ProTree1(BTree T) {
  SqStack *S;
  S=InitStack();  //初始化栈S 
  BNode *p=T;   //p指针从二叉树的根结点开始 
  while(p!=NULL||!StackEmpty(*S)) { //当栈S不为空或遍历指针p不为空时循环一直进行下去 
  if(p!=NULL) { //左子树遍历 
    printf("%c ",p->data);  //访问当前结点 
    StackPush(S,p);   //当前结点入栈 
    p=p->lchild;    //继续向左孩子 
  } else {  //右子树遍历 
    StackPop(S,p);    //栈顶元素出栈 
    p=p->rchild;    //继续向右孩子 
  }
  }
}


完整代码:

#include <stdio.h>
#include <malloc.h>
#define MAXSIZE 100
/*1、二叉树的定义*/
typedef struct BNode {
  char data;  //数据域,类型为char
  struct BNode *lchild,*rchild;  //左孩子、右孩子指针
} *BTree;
/*2、顺序栈的定义*/
typedef struct SqStack {
  BTree data[MAXSIZE];  //存放栈中元素 ,使用数组,数组类型为BTree
  int top;    //栈顶指针 ,记录栈顶元素的位置
} *Stack; //顺序栈的类型定义
/*3、判断顺序栈是否为空*/
bool StackEmpty(Stack S) {
  if(S->top==-1)  //当top=-1时,栈为空,而并不是S.top=0(它表示的是栈中有一个元素).
  return true;
  else
  return false;
}
/*4、顺序栈的初始化,初始化一个空栈*/
Stack InitStack() {
  Stack S;
  S=(Stack)malloc(sizeof(SqStack)); //定义一个根结点S
  S->top=-1;  //顺序栈为空
  return S;
}
/*5、进栈*/
bool StackPush(Stack S,BTree p) {
  if(S->top==MAXSIZE-1) //若栈已满,则报错
  return false;
  ++S->top;    //top指针始终指向栈顶,新的元素进栈,所以指针先加1
  S->data[S->top]=p;  //将进栈元素的值传入并入栈
  //S->data[++S->top]=p;  //也可以用这一句代码替换上面两行代码
  return true;
}
/*6、出栈*/
bool StackPop(Stack S,BTree &p) {
  if(S->top==-1)    //若栈为空,则报错
  return false;
  p=S->data[S->top];  //出栈
  S->top--;    //指针减1
  //p=S->data[S->top--];  //也可以用这一句代码替换上面两行代码
  return true;
}
/*7、二叉树的建立*/
BTree CreateTree() {
  BTree T;
  char ch;
  scanf("%c",&ch);
  getchar();  //getchar()用于接收每次输入字符结点后的回车符,从而以便输入下一个字符结点
  if(ch=='0') //当为0时,将结点置空
  T=NULL;
  else {
  T=(BTree)malloc(sizeof(BTree)); //分配一个新的结点
  T->data=ch;
  printf("请输入%c结点的左孩子结点:",T->data);
  T->lchild=CreateTree();  //通过递归建立左孩子结点
  printf("请输入%c结点的右孩子结点:",T->data);
  T->rchild=CreateTree();  //通过递归建立右孩子结点
  }
  return T;
}
/*8、广义表输出二叉树*/
void ShowTree(BTree T) {
  if(T!=NULL) {
  //当二叉树不为空时
  printf("%c",T->data); //输入出该结点的数据域
  if(T->lchild!=NULL) {  //若该结点的左子树不为空
    printf("(");  //输出一个左括号
    ShowTree(T->lchild);  //通过递归继续输出结点的左子树结点下的各结点
    if(T->rchild!=NULL) { //若该结点右子树不为空
    printf(",");  //输出一个逗号
    ShowTree(T->rchild);  //通过递归继续输出结点的右子树结点下的各结点
    }
    printf(")");  //输出一个右括号
  } else {  //若左子树为空,右子树不为空
    if(T->rchild!=NULL) {
    printf("(");  //输出一个左括号
    ShowTree(T->lchild);  //通过递归继续输出结点的左子树结点下的各结点
    if(T->rchild!=NULL) {  //若该结点的右子树不为空
      printf(",");  //输出一个逗号
      ShowTree(T->rchild);  //通过递归继续输出结点的右子树结点下的各结点
    }
    printf(")");  //输出一个右括号
    }
  }
  }
}
/*9、先序遍历二叉树【非递归】*/
void ProTree1(BTree T) {
  SqStack *S;
  S=InitStack();  //初始化栈S
  BNode *p=T;   //p指针从二叉树的根结点开始
  while(p!=NULL||!StackEmpty(S)) {  //当栈S不为空或遍历指针p不为空时循环一直进行下去
  if(p!=NULL) { //左子树遍历
    printf("%c ",p->data);  //访问当前结点
    StackPush(S,p);   //当前结点入栈
    p=p->lchild;    //继续向左孩子
  } else {  //右子树遍历
    StackPop(S,p);    //栈顶元素出栈
    p=p->rchild;    //继续向右孩子
  }
  }
}
/*主函数*/
int main() {
  BTree T;
  T=NULL;
  printf("请输入二叉树的根结点:");
  T=CreateTree();  //建立二叉树
  printf("建立的二叉树如下:\n");
  ShowTree(T);  //通过广义表显示二叉树
  printf("\n");
  printf("先序:\n");
  ProTree1(T);  //非递归先序遍历二叉树
}


运行结果如下:

1667293354069.jpg


(二)中序遍历二叉树的非递归算法


二叉树的中序遍历(LDR)

二叉树的中序遍历中,首先是遍历完根结点的左子树,然后是根结点,最后遍历完根结点的右子树,依次下去至所有结点都遍历到。


分析一下具体的实现步骤:

由于我们要自己定义一个栈,初始时,栈为空。首先,结点A入栈,看根结点A的左孩子,左孩子存在为结点B,结点B入栈,当前栈内序列由下至上为AB;然后看结点B的左孩子,左孩子存在为结点D,结点D入栈,当前栈内序列由下至上为ABD;由于结点D的左孩子不存在,此时栈顶元素出栈,即结点D出栈,当前输出遍历序列为D;结点D无右孩子结点,此时栈顶元素出栈,即结点B出栈,当前输出遍历序列为DB;然后遍历结点B的右子树,由于结点B的右孩子存在为结点E,结点E入栈,当前栈内序列由下至上为AE,由于结点E的左孩子不存在,此时栈顶元素出栈,当前输出遍历序列为DBE;结点E无右孩子结点,此时栈顶元素出栈,即结点A出栈,当前输出遍历序列为DBEA,根结点A的左子树和根结点遍历完成,此时遍历右子树。看根结点A的右孩子,右孩子存在为结点C,结点C入栈,当前栈内序列为C,看结点C的左孩子,左孩子存在为结点F,结点F入栈,当前栈内序列由下至上为CF,然后看结点F的左孩子;由于结点F的左孩子不存在,此时栈顶元素出栈,即结点F出栈,当前输出遍历序列为DBEAF;结点F无右孩子结点,此时栈顶元素出栈,即结点C出栈,当前输出遍历序列为DBEAFC;然后遍历结点C的右子树,由于结点C的右孩子存在为结点G,结点G入栈,由于结点G的左孩子不存在,此时栈顶元素出栈,即结点G出栈,其右孩子不存在,最终栈为空,输出遍历序列为DBEAFCG。

1667293366382.jpg

由于中序遍历是先遍历左子树,依次根结点,然后右子树,所以在当前结点的左孩子为空时,栈顶元素出栈,输出当前结点,只需对先序遍历的非递归代码进行部分改动,中序遍历二叉树的非递归算法代码如下:

/*中序遍历二叉树【非递归】*/
void InTree1(BTree T) {
  SqStack *S;
  S=InitStack();  //初始化栈S
  BNode *p=T;   //p指针从二叉树的根结点开始
  while(p!=NULL||!StackEmpty(*S)) { //当栈S不为空或遍历指针p不为空时循环一直进行下去
  if(p!=NULL) { //左子树遍历
    StackPush(S,p);   //当前结点入栈
    p=p->lchild;    //继续向左孩子
  } else {  //右子树遍历
    StackPop(S,p);    //栈顶元素出栈
    printf("%c ",p->data);  //访问当前结点
    p=p->rchild;    //继续向右孩子
  }
  }
}

完整代码:

#include <stdio.h>
#include <malloc.h>
#define MAXSIZE 100
/*1、二叉树的定义*/
typedef struct BNode {
  char data;  //数据域,类型为char
  struct BNode *lchild,*rchild;  //左孩子、右孩子指针
} *BTree;
/*2、顺序栈的定义*/
typedef struct SqStack {
  BTree data[MAXSIZE];  //存放栈中元素 ,使用数组,数组类型为BTree
  int top;    //栈顶指针 ,记录栈顶元素的位置
} *Stack; //顺序栈的类型定义
/*3、判断顺序栈是否为空*/
bool StackEmpty(Stack S) {
  if(S->top==-1)  //当top=-1时,栈为空,而并不是S.top=0(它表示的是栈中有一个元素).
  return true;
  else
  return false;
}
/*4、顺序栈的初始化,初始化一个空栈*/
Stack InitStack() {
  Stack S;
  S=(Stack)malloc(sizeof(SqStack)); //定义一个根结点S
  S->top=-1;  //顺序栈为空
  return S;
}
/*5、进栈*/
bool StackPush(Stack S,BTree p) {
  if(S->top==MAXSIZE-1) //若栈已满,则报错
  return false;
  ++S->top;    //top指针始终指向栈顶,新的元素进栈,所以指针先加1
  S->data[S->top]=p;  //将进栈元素的值传入并入栈
  //S->data[++S->top]=p;  //也可以用这一句代码替换上面两行代码
  return true;
}
/*6、出栈*/
bool StackPop(Stack S,BTree &p) {
  if(S->top==-1)    //若栈为空,则报错
  return false;
  p=S->data[S->top];  //出栈
  S->top--;    //指针减1
  //p=S->data[S->top--];  //也可以用这一句代码替换上面两行代码
  return true;
}
/*7、二叉树的建立*/
BTree CreateTree() {
  BTree T;
  char ch;
  scanf("%c",&ch);
  getchar();  //getchar()用于接收每次输入字符结点后的回车符,从而以便输入下一个字符结点
  if(ch=='0') //当为0时,将结点置空
  T=NULL;
  else {
  T=(BTree)malloc(sizeof(BTree)); //分配一个新的结点
  T->data=ch;
  printf("请输入%c结点的左孩子结点:",T->data);
  T->lchild=CreateTree();  //通过递归建立左孩子结点
  printf("请输入%c结点的右孩子结点:",T->data);
  T->rchild=CreateTree();  //通过递归建立右孩子结点
  }
  return T;
}
/*8、广义表输出二叉树*/
void ShowTree(BTree T) {
  if(T!=NULL) {
  //当二叉树不为空时
  printf("%c",T->data); //输入出该结点的数据域
  if(T->lchild!=NULL) {  //若该结点的左子树不为空
    printf("(");  //输出一个左括号
    ShowTree(T->lchild);  //通过递归继续输出结点的左子树结点下的各结点
    if(T->rchild!=NULL) { //若该结点右子树不为空
    printf(",");  //输出一个逗号
    ShowTree(T->rchild);  //通过递归继续输出结点的右子树结点下的各结点
    }
    printf(")");  //输出一个右括号
  } else {  //若左子树为空,右子树不为空
    if(T->rchild!=NULL) {
    printf("(");  //输出一个左括号
    ShowTree(T->lchild);  //通过递归继续输出结点的左子树结点下的各结点
    if(T->rchild!=NULL) {  //若该结点的右子树不为空
      printf(",");  //输出一个逗号
      ShowTree(T->rchild);  //通过递归继续输出结点的右子树结点下的各结点
    }
    printf(")");  //输出一个右括号
    }
  }
  }
}
/*9、先序遍历二叉树【非递归】*/
void ProTree1(BTree T) {
  SqStack *S;
  S=InitStack();  //初始化栈S
  BNode *p=T;   //p指针从二叉树的根结点开始
  while(p!=NULL||!StackEmpty(S)) {  //当栈S不为空或遍历指针p不为空时循环一直进行下去
  if(p!=NULL) { //左子树遍历
    printf("%c ",p->data);  //访问当前结点
    StackPush(S,p);   //当前结点入栈
    p=p->lchild;    //继续向左孩子
  } else {  //右子树遍历
    StackPop(S,p);    //栈顶元素出栈
    p=p->rchild;    //继续向右孩子
  }
  }
}
/*10、中序遍历二叉树【非递归】*/
void InTree1(BTree T) {
  SqStack *S;
  S=InitStack();  //初始化栈S
  BNode *p=T;   //p指针从二叉树的根结点开始
  while(p!=NULL||!StackEmpty(S)) {  //当栈S不为空或遍历指针p不为空时循环一直进行下去
  if(p!=NULL) { //左子树遍历
    StackPush(S,p);   //当前结点入栈
    p=p->lchild;    //继续向左孩子
  } else {  //右子树遍历
    StackPop(S,p);    //栈顶元素出栈
    printf("%c ",p->data);  //访问当前结点
    p=p->rchild;    //继续向右孩子
  }
  }
}
/*主函数*/
int main() {
  BTree T;
  T=NULL;
  printf("请输入二叉树的根结点:");
  T=CreateTree();  //建立二叉树
  printf("建立的二叉树如下:\n");
  ShowTree(T);  //通过广义表显示二叉树
  printf("\n");
  printf("先序:\n");
  ProTree1(T);  //非递归先序遍历二叉树
  printf("\n");
  printf("中序:\n");
  InTree1(T);   //非递归中序遍历二叉树
}


运行结果如下:

1667293403206.jpg


(三)后序遍历二叉树的非递归算法


二叉树的后序遍历(LRD)

二叉树的后序遍历中,首先是遍历完根结点的左子树,然后遍历完根结点的右子树,最后是根结点,依次下去至所有结点都遍历到,也就是从二叉树的底层往上层依次遍历。


根据前面的思想,以及后序遍历的规则,应该从二叉树的根结点的左孩子开始,直到访问左孩子为空;然后读取当前栈顶元素,若当前根结点的右孩子不为空且未被访问,则开始转为右孩子,直到访问右孩子为空,否则栈顶元素出栈。

/*读取栈顶元素*/
bool StackGetTop(Stack S,BTree &p) {
  if(S->top==-1)    //若栈为空,则报错
  return false;
  p=S->data[S->top];  //p记录栈顶元素
  return true;
}


由于要记录当前返回的是左子树还是右子树,以下采用一个辅助指针t,指向最近访问过的结点,如下是后序遍历二叉树的非递归算法代码:

/*后序遍历二叉树【非递归】*/
void PostTree1(BTree T) {
  SqStack *S;
  S=InitStack();  //初始化栈S
  BNode *p=T;   //p指针从二叉树的根结点开始
  BNode *t=NULL;
  while(p!=NULL||!StackEmpty(S)) {  //当栈S不为空或遍历指针p不为空时循环一直进行下去
  if(p!=NULL) {    //左子树遍历
    StackPush(S,p);   //当前结点入栈
    p=p->lchild;    //继续向左孩子
  } else {      //右子树遍历
    StackGetTop(S,p);  //读取当前栈顶结点 
    if(p->rchild&&p->rchild!=t)  //若当前结点的右子树存在,且未被访问过 
    p=p->rchild;    //转向开始访问右子树 
    else {
    StackPop(S,p);    //栈顶元素出栈
    printf("%c ",p->data);  //访问当前结点
    t=p;      //t指针记录最近访问的结点 
    p=NULL;     //访问后,重置p指针 
    }
  }
  }
}


1667293440394.jpg

完整代码:

#include <stdio.h>
#include <malloc.h>
#define MAXSIZE 100
/*1、二叉树的定义*/
typedef struct BNode {
  char data;  //数据域,类型为char
  struct BNode *lchild,*rchild;  //左孩子、右孩子指针
} *BTree;
/*2、顺序栈的定义*/
typedef struct SqStack {
  BTree data[MAXSIZE];  //存放栈中元素 ,使用数组,数组类型为BTree
  int top;    //栈顶指针 ,记录栈顶元素的位置
} *Stack; //顺序栈的类型定义
/*3、判断顺序栈是否为空*/
bool StackEmpty(Stack S) {
  if(S->top==-1)  //当top=-1时,栈为空,而并不是S.top=0(它表示的是栈中有一个元素).
  return true;
  else
  return false;
}
/*4、顺序栈的初始化,初始化一个空栈*/
Stack InitStack() {
  Stack S;
  S=(Stack)malloc(sizeof(SqStack)); //定义一个根结点S
  S->top=-1;  //顺序栈为空
  return S;
}
/*5、进栈*/
bool StackPush(Stack S,BTree p) {
  if(S->top==MAXSIZE-1) //若栈已满,则报错
  return false;
  ++S->top;    //top指针始终指向栈顶,新的元素进栈,所以指针先加1
  S->data[S->top]=p;  //将进栈元素的值传入并入栈
  //S->data[++S->top]=p;  //也可以用这一句代码替换上面两行代码
  return true;
}
/*6、出栈*/
bool StackPop(Stack S,BTree &p) {
  if(S->top==-1)    //若栈为空,则报错
  return false;
  p=S->data[S->top];  //出栈
  S->top--;    //指针减1
  //p=S->data[S->top--];  //也可以用这一句代码替换上面两行代码
  return true;
}
/*7、读取栈顶元素*/
bool StackGetTop(Stack S,BTree &p) {
  if(S->top==-1)    //若栈为空,则报错
  return false;
  p=S->data[S->top];  //p记录栈顶元素
  return true;
}
/*8、二叉树的建立*/
BTree CreateTree() {
  BTree T;
  char ch;
  scanf("%c",&ch);
  getchar();  //getchar()用于接收每次输入字符结点后的回车符,从而以便输入下一个字符结点
  if(ch=='0') //当为0时,将结点置空
  T=NULL;
  else {
  T=(BTree)malloc(sizeof(BTree)); //分配一个新的结点
  T->data=ch;
  printf("请输入%c结点的左孩子结点:",T->data);
  T->lchild=CreateTree();  //通过递归建立左孩子结点
  printf("请输入%c结点的右孩子结点:",T->data);
  T->rchild=CreateTree();  //通过递归建立右孩子结点
  }
  return T;
}
/*9、广义表输出二叉树*/
void ShowTree(BTree T) {
  if(T!=NULL) {
  //当二叉树不为空时
  printf("%c",T->data); //输入出该结点的数据域
  if(T->lchild!=NULL) {  //若该结点的左子树不为空
    printf("(");  //输出一个左括号
    ShowTree(T->lchild);  //通过递归继续输出结点的左子树结点下的各结点
    if(T->rchild!=NULL) { //若该结点右子树不为空
    printf(",");  //输出一个逗号
    ShowTree(T->rchild);  //通过递归继续输出结点的右子树结点下的各结点
    }
    printf(")");  //输出一个右括号
  } else {  //若左子树为空,右子树不为空
    if(T->rchild!=NULL) {
    printf("(");  //输出一个左括号
    ShowTree(T->lchild);  //通过递归继续输出结点的左子树结点下的各结点
    if(T->rchild!=NULL) {  //若该结点的右子树不为空
      printf(",");  //输出一个逗号
      ShowTree(T->rchild);  //通过递归继续输出结点的右子树结点下的各结点
    }
    printf(")");  //输出一个右括号
    }
  }
  }
}
/*10、先序遍历二叉树【非递归】*/
void ProTree1(BTree T) {
  SqStack *S;
  S=InitStack();  //初始化栈S
  BNode *p=T;   //p指针从二叉树的根结点开始
  while(p!=NULL||!StackEmpty(S)) {  //当栈S不为空或遍历指针p不为空时循环一直进行下去
  if(p!=NULL) { //左子树遍历
    printf("%c ",p->data);  //访问当前结点
    StackPush(S,p);   //当前结点入栈
    p=p->lchild;    //继续向左孩子
  } else {  //右子树遍历
    StackPop(S,p);    //栈顶元素出栈
    p=p->rchild;    //继续向右孩子
  }
  }
}
/*11、中序遍历二叉树【非递归】*/
void InTree1(BTree T) {
  SqStack *S;
  S=InitStack();  //初始化栈S
  BNode *p=T;   //p指针从二叉树的根结点开始
  while(p!=NULL||!StackEmpty(S)) {  //当栈S不为空或遍历指针p不为空时循环一直进行下去
  if(p!=NULL) { //左子树遍历
    StackPush(S,p);   //当前结点入栈
    p=p->lchild;    //继续向左孩子
  } else {  //右子树遍历
    StackPop(S,p);    //栈顶元素出栈
    printf("%c ",p->data);  //访问当前结点
    p=p->rchild;    //继续向右孩子
  }
  }
}
/*12、后序遍历二叉树【非递归】*/
void PostTree1(BTree T) {
  SqStack *S;
  S=InitStack();  //初始化栈S
  BNode *p=T;   //p指针从二叉树的根结点开始
  BNode *t=NULL;
  while(p!=NULL||!StackEmpty(S)) {  //当栈S不为空或遍历指针p不为空时循环一直进行下去
  if(p!=NULL) {    //左子树遍历
    StackPush(S,p);   //当前结点入栈
    p=p->lchild;    //继续向左孩子
  } else {      //右子树遍历
    StackGetTop(S,p);  //读取当前栈顶结点 
    if(p->rchild&&p->rchild!=t)  //若当前结点的右子树存在,且未被访问过 
    p=p->rchild;    //转向开始访问右子树 
    else {
    StackPop(S,p);    //栈顶元素出栈
    printf("%c ",p->data);  //访问当前结点
    t=p;      //t指针记录最近访问的结点 
    p=NULL;     //访问后,重置p指针 
    }
  }
  }
}
/*主函数*/
int main() {
  BTree T;
  T=NULL;
  printf("请输入二叉树的根结点:");
  T=CreateTree();  //建立二叉树
  printf("建立的二叉树如下:\n");
  ShowTree(T);  //通过广义表显示二叉树
  printf("\n");
  printf("先序:\n");
  ProTree1(T);  //非递归先序遍历二叉树
  printf("\n");
  printf("中序:\n");
  InTree1(T);
  printf("\n");
  printf("后序:\n");
  PostTree1(T);
}

运行结果如下:

1667293010974.jpg

相关文章
|
12天前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
36 12
|
12天前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
37 10
|
12天前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
37 2
|
26天前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
12天前
|
数据采集 存储 算法
【C++数据结构——图】图的遍历(头歌教学实验平台习题) 【合集】
本文介绍了图的遍历算法,包括深度优先遍历(DFS)和广度优先遍历(BFS)。深度优先遍历通过递归方式从起始节点深入探索图,适用于寻找路径、拓扑排序等场景;广度优先遍历则按层次逐层访问节点,适合无权图最短路径和网络爬虫等应用。文中提供了C++代码示例,演示了如何实现这两种遍历方法,并附有测试用例及结果,帮助读者理解和实践图的遍历算法。
26 0
|
2月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
286 9
|
2月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
44 1
|
12天前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
128 75
|
12天前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
34 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
12天前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
35 9

热门文章

最新文章