其他分享
首页 > 其他分享> > 二叉树的之字形遍历

二叉树的之字形遍历

作者:互联网

给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)
例如:
给定的二叉树是{3,9,20,15,7,#,#},
在这里插入图片描述

该二叉树之字形层序遍历的结果是
3
20 9
15 7

输入格式
一行字符串,只包含大写字母和#。
此处采用完全二叉树的顺序存储结构

输出格式
若干行,之字形输出树的结点,每一行输出树的一层。

输入样例
ABC###D##

输出样例
A
C B
D

基本思路:
1、通过观察题意,我们可以发现,这个题目构造二叉树的时候可以通过完全二叉树的顺序存储结构进行构造.
那么问题来了,怎样来通过顺序存储结构来构建二叉树?其实,通过顺序存储结构构建二叉树最大的问题在于我们怎么知道当前这个节点的左右子节点,或者这个节点父节点。通过学习,我们可以将当前节点的下标定位i,那么它的左子节点的下标就是2 * i,右子节点就是2 * i + 1,父节点的下标就是 i / 2。(值得注意的是,如果是这样的话,那么需要定义一个节点数组,并且节点是从下标为1开始算起的。否则,如果节点的下标从0开始的话,那么左子节点的下标就是2 * i +1,右子节点的下标就是2 * i + 2。

顺序存储结构构建二叉树对应的代码:

// 按先序次序输入二叉树中结点的值(一个字符),’#’字符表示空树,
Status CreateBiTree(Node &T,char *msg) { 
  int i,len = strlen(msg);
  char ch;
  Node arr[MAX_SIZE];//定义结构体指针数组,用于存放节点
  int check[MAX_SIZE] = {0};//check数组用于表示当前下标的节点是否为其他节点的子节点,值为1表示是,否则不是
  for(i = 1; i <= len;i++){
      ch = msg[i - 1];
      if (ch=='#') arr[i] = NULL;//如果字符为#,表示空树,将当前节点变成NULL
      else {
        arr[i] = (Node)malloc(sizeof(struct BiTNode))
        if (arr[i] == NULL){ 
           printf("创建节点失败!!!\n");
           return ERROR;
        }
        arr[i]->data = ch; // 生成节点,并给当前这个节点赋值
        arr[i]->lchild = arr[i]->rchild = NULL;
      }
  }
  for(i = 1; i <= len / 2; i++){
      if(arr[i] == NULL)//如果当前节点为NULL,那么就不需要构建它的子节点,直接跳到更换循环变量i这一步(即执行i++)
        continue;
        /*
        这里之所以不需要判断i * 2 <= len,是因为for循环中已经做好了判断,
        如果当前这个i <= len / 2,那么i * 2始终不会发生越界
        */
      arr[i]->lchild = arr[i * 2];
      check[i * 2] = 1;
      if(i * 2 + 1 <= len) { 
      //这里之所以要这样,是因为避免i * 2 + 1发生越界,从而发生错误
        arr[i]->rchild = arr[i * 2 + 1];
        check[i * 2 + 1] = 1;
       }
  }
  /*
  将根节点复制给T(因为输入的是一个先序序列,那么第一个必然是根节点,所以
  arr[1]就是根节点,如果输入的不是不是先序序列,那么我们需要定义一个check
  数组,表示这个节点是否为其他节点的子节点,如果值为0,表示不是其他节点的
  子节点,也就是说这个节点是二叉树的根节点,否则,值为1的时候,表示这个节
  点是其他节点的子节点
  */
  /*
  //通过check数组查找根节点(如果输入的字符串不是先序序列的时候,就需要查找根节点)
  for(i = 1; i <= len; i++){
     if(check[i] == 0) //值为0,表示当前下标对应的节点是根节点
         break;
  }
  T = arr[i];
  */
  T = arr[i];
  return OK;
} 

2、字形遍历:
基本思路:通过观察输出样例,我们可以发现,如果是奇数层,他是从左到右开始遍历的,反之,如果是偶数层,那么他是从右到左开始遍历的。那么问题来了,我们需要怎样实现呢?
起初我的思路是,这个问题本质上依旧是一个层序遍历,只是入队的时候需要改变一下顺序一下即可。于是写好代码并运行:

如果是通过队列实现,只是通过改变入队的顺序的话,那么对应的样例:
ABCDEFGHIJ,那么本应该输出:
在这里插入图片描述

错误代码(思路错误)

#include "stdio.h"
#include "malloc.h"
#include<string.h>
#include<stdlib.h>
#define OK  1
#define ERROR  0
#define MAX_SIZE 100
typedef int  Status;

typedef char  ElemType;
typedef struct BiTNode * Node;
typedef Node * List;
struct BiTNode{
  ElemType data;
  Node lchild,rchild;//左右孩子指针
};

typedef struct Queue{
   List arr;
   int front;
   int rear;
}Queue;
int init(Queue &queue){
   queue.arr = (List)malloc(sizeof(List) * MAX_SIZE);
   if(queue.arr == NULL){
     printf("创建队列错误!!!/n");
     exit(0);
   }
   int i;
   for(i = 0; i < MAX_SIZE; i++){
     queue.arr[i] = (Node)malloc(sizeof(struct BiTNode));
     if(queue.arr[i] == NULL){
        printf("创建节点失败!!!\n");
        exit(0);
     }
   }
   queue.front = queue.rear = 0;
   return OK;
}

int push(Queue &queue,Node e){
   if((queue.rear + 1) % MAX_SIZE == queue.front){
     printf("队列为满!!!\n");
     exit(0);
   }
   queue.arr[queue.rear++] = e;
   queue.rear %= MAX_SIZE;
   return OK;
}
int pop(Queue &queue,Node &e){
   if(queue.front == queue.rear){
     printf("栈为空!!!\n");
     exit(0);
   }
   e = queue.arr[queue.front++];
   queue.front %= MAX_SIZE;
   return OK;
}
int isEmpty(Queue &queue){
   return queue.front == queue.rear;
}
int getSize(Queue &queue){
   return (queue.rear - queue.front + MAX_SIZE) % MAX_SIZE;
}

// 按先序次序输入二叉树中结点的值(一个字符),’#’字符表示空树,
Status CreateBiTree(Node &T,char *msg) {
  int i,len = strlen(msg);
  char ch;
  Node arr[MAX_SIZE];//定义结构体指针数组,用于存放节点
  int check[MAX_SIZE] = {0};//check数组用于表示当前下标的节点是否为其他节点的子节点,值为1表示是,否则不是
  for(i = 1; i <= len;i++){
      ch = msg[i - 1];
      if (ch=='#') arr[i] = NULL;//如果字符为#,表示空树,将当前节点变成NULL
      else {
        arr[i] = (Node)malloc(sizeof(struct BiTNode));
        if (arr[i] == NULL){
           printf("创建节点失败!!!\n");
           return ERROR;
        }
        arr[i]->data = ch; // 生成节点,并给当前这个节点赋值
        arr[i]->lchild = arr[i]->rchild = NULL;
      }
  }
  for(i = 1; i <= len / 2; i++){
      if(arr[i] == NULL)//如果当前节点为NULL,那么就不需要构建它的子节点,直接跳到更换循环变量i这一步(即执行i++)
        continue;
        /*
        这里之所以不需要判断i * 2 <= len,是因为for循环中已经做好了判断,
        如果当前这个i <= len / 2,那么i * 2始终不会发生越界
        */
      arr[i]->lchild = arr[i * 2];
      check[i * 2] = 1;
      if(i * 2 + 1 <= len) {
      //这里之所以要这样,是因为避免i * 2 + 1发生越界,从而发生错误
        arr[i]->rchild = arr[i * 2 + 1];
        check[i * 2 + 1] = 1;
       }
  }
  /*
  将根节点复制给T(因为输入的是一个先序序列,那么第一个必然是根节点,所以
  arr[1]就是根节点,如果输入的不是不是先序序列,那么我们需要定义一个check
  数组,表示这个节点是否为其他节点的子节点,如果值为0,表示不是其他节点的
  子节点,也就是说这个节点是二叉树的根节点,否则,值为1的时候,表示这个节
  点是其他节点的子节点
  */

  for(i = 1; i <= len; i++){
     if(check[i] == 0) //值为0,表示当前下标对应的节点是根节点
         break;
  }
  T = arr[i];

 // T = arr[i];
  return OK;
}
void display(Node root){
   if(root != NULL){
     printf("%5c",root->data);
     display(root->lchild);
     display(root->rchild);
   }
}
//二叉树的字行遍历
void levelTraversal2(Node root){
   Queue queue;
   init(queue);
   push(queue,root);
   Node t;
   int size,i = 1,j;//i表示第几层
   while(!isEmpty(queue)){
      size = getSize(queue);//获取队列的元素个数
      for(j = 1; j <= size; j++){
          pop(queue,t);//从队列中跳出一个节点,并赋值给t
          printf("%c ",t->data);
          if(i % 2 != 0){
            //如果i是奇数,那么先将右子节点压入到队列中,再是右子节点
              if(t->rchild != NULL)
                push(queue,t->rchild);
              if(t->lchild != NULL)
                push(queue,t->lchild);
         }else{
                //如果i是偶数,那么需要先将左子节点压入到队列中
              if(t->lchild != NULL)
                push(queue,t->lchild);
              if(t->rchild != NULL)
                push(queue,t->rchild);
         }
      }
      printf("\n");
      i++;
   }
}
int main()   //主函数
{
   Node root;
   char msg[81];
   gets(msg);
   CreateBiTree(root,msg);//创建二叉树

  // display(root);//前序遍历二叉树,判断是否创建树成功
  // printf("\n");
   levelTraversal2(root);//字形遍历
   return 0;
}//main


在这里插入图片描述
通过比对输出样例,我们可以发现第三行之后就开始错误了。
咩哇,点解会这样咧?通过队列实现的时候,我们明明改变了入队的顺序了,但是得到的结果却不是我们想要的。后来仔细想了下,原来是这样的,我们队列中已经有了C、B,并且如果出队的时候,我们会先将C跳出,然后判断i是否为奇数,如果是,那么就先将C的右子节点压入队列中,然后才将左子节点压入到队列中,否则i是偶数,那么先将C的左子节点压入队列中,然后才将右子节点压入队列中,然后继续从队列中跳出B,重复上面的步骤。最后队列中的节点分别是F G D E,所以下一次遍历的时候输出的是F G D E,同样的会影响下一层

所以,通过队列来实现之字形遍历这个方法是行不通的,那么我们需要通过栈来实现,并且需要通过两个栈来实现。这时候小伙伴们有会问了,为什么不可以只使用一个栈呢?其实我们都知道,栈是一个先进后出的数据结构,那么即便我们早先已经知道了当前栈的元素个数size,然后通过for循环来将栈中的元素遍历,但是遍历的时候我们需要将跳出栈的节点的左右子节压入到栈中,所以下一次跳出栈的就是在for循环中刚刚入栈的节点,而不再是我们预想的节点了,所以不可以通过一个栈来实现

过程分析:
在这里插入图片描述

正确代码:

#include "stdio.h"
#include "malloc.h"
#include<string.h>
#include<stdlib.h>
#define OK  1
#define ERROR  0
#define MAX_SIZE 100
typedef int  Status;

typedef char  ElemType;
typedef struct BiTNode * Node;
typedef Node * List;
struct BiTNode{
  ElemType data;
  Node lchild,rchild;//左右孩子指针
};

typedef struct STACK{
   List arr;
   int top;
}Stack;
int initStack(Stack &stack){
   stack.arr = (List)malloc(sizeof(List) * MAX_SIZE);
   if(stack.arr == NULL){
     printf("创建栈错误!!!/n");
     exit(0);
   }
   int i;
   for(i = 0; i < MAX_SIZE; i++){
     stack.arr[i] = (Node)malloc(sizeof(struct BiTNode));
     if(stack.arr[i] == NULL){
        printf("创建节点失败!!!\n");
        exit(0);
     }
   }
   stack.top = 0;
   return OK;
}

int pushStack(Stack &stack,Node e){
   if(stack.top == MAX_SIZE){
     printf("栈为满!!!\n");
     exit(0);
   }
   stack.arr[stack.top++] = e;
   return OK;
}
int popStack(Stack &stack,Node &e){
   if(stack.top == 0){
     printf("栈为空!!!\n");
     exit(0);
   }
   e = stack.arr[--stack.top];
   return OK;
}
int isStackEmpty(Stack &stack){
   return stack.top == 0;
}
int getStackSize(Stack &stack){
   return stack.top;
}

// 按先序次序输入二叉树中结点的值(一个字符),’#’字符表示空树,
Status CreateBiTree(Node &T,char *msg) {
  int i,len = strlen(msg);
  char ch;
  Node arr[MAX_SIZE];//定义结构体指针数组,用于存放节点
  int check[MAX_SIZE] = {0};//check数组用于表示当前下标的节点是否为其他节点的子节点,值为1表示是,否则不是
  for(i = 1; i <= len;i++){
      ch = msg[i - 1];
      if (ch=='#') arr[i] = NULL;//如果字符为#,表示空树,将当前节点变成NULL
      else {
        arr[i] = (Node)malloc(sizeof(struct BiTNode));
        if (arr[i] == NULL){
           printf("创建节点失败!!!\n");
           return ERROR;
        }
        arr[i]->data = ch; // 生成节点,并给当前这个节点赋值
        arr[i]->lchild = arr[i]->rchild = NULL;
      }
  }
  for(i = 1; i <= len / 2; i++){
      if(arr[i] == NULL)//如果当前节点为NULL,那么就不需要构建它的子节点,直接跳到更换循环变量i这一步(即执行i++)
        continue;
        /*
        这里之所以不需要判断i * 2 <= len,是因为for循环中已经做好了判断,
        如果当前这个i <= len / 2,那么i * 2始终不会发生越界
        */
      arr[i]->lchild = arr[i * 2];
      check[i * 2] = 1;
      if(i * 2 + 1 <= len) {
      //这里之所以要这样,是因为避免i * 2 + 1发生越界,从而发生错误
        arr[i]->rchild = arr[i * 2 + 1];
        check[i * 2 + 1] = 1;
       }
  }
  /*
  将根节点复制给T(因为输入的是一个先序序列,那么第一个必然是根节点,所以
  arr[1]就是根节点,如果输入的不是不是先序序列,那么我们需要定义一个check
  数组,表示这个节点是否为其他节点的子节点,如果值为0,表示不是其他节点的
  子节点,也就是说这个节点是二叉树的根节点,否则,值为1的时候,表示这个节
  点是其他节点的子节点
  */

  for(i = 1; i <= len; i++){
     if(check[i] == 0) //值为0,表示当前下标对应的节点是根节点
         break;
  }
  T = arr[i];

 // T = arr[i];
  return OK;
}
void display(Node root){
   if(root != NULL){
     printf("%5c",root->data);
     display(root->lchild);
     display(root->rchild);
   }
}
//二叉树的字行遍历
void levelTraversal2(Node root){
   Stack stack1,stack2,tmp;
   initStack(stack1);//初始化栈
   initStack(stack2);
   pushStack(stack1,root);
   Node t;
   int size,i = 1,j;//i表示第几层
   while(!isStackEmpty(stack1) || !isStackEmpty(stack2)){
        //判断那个栈式空的
     if(!isStackEmpty(stack1)){
         while(!isStackEmpty(stack1)){
        //如果stack1不为空,那么就将节点从栈stack1中跳出节点,然后将这个节点的子节点压入到栈stack2中
          popStack(stack1,t);
          printf("%c ",t->data);
          if(i % 2 != 0){
            //如果i是奇数,那么先将左子节点压入到栈2中,再是右子节点
              if(t->lchild != NULL)
                pushStack(stack2,t->lchild);
              if(t->rchild != NULL)
                pushStack(stack2,t->rchild);
         }else{
                //如果i是偶数层,那么需要将右子节点压入到栈2中,再是左子节点
              if(t->rchild != NULL)
                pushStack(stack2,t->rchild);
              if(t->lchild != NULL)
                pushStack(stack2,t->lchild);
         }
      }
     }else{
      while(!isStackEmpty(stack2)){
        //如果stack2不为空,那么就将节点从栈stack2中跳出节点,然后将这个节点的子节点压入到栈stack1中
          popStack(stack2,t);
          printf("%c ",t->data);
          if(i % 2 != 0){
            //如果i是奇数层,那么先将左子节点压入到栈1中,再是右子节点
              if(t->lchild != NULL)
                pushStack(stack1,t->lchild);
              if(t->rchild != NULL)
                pushStack(stack1,t->rchild);
         }else{
                //如果i是偶数,那么需要将右子节点压入到栈1中,再是左子节点
              if(t->rchild != NULL)
                pushStack(stack1,t->rchild);
              if(t->lchild != NULL)
                pushStack(stack1,t->lchild);
         }
      }
     }
      printf("\n");
      i++;
   }
}
int main()   //主函数
{
   Node root;
   char msg[81];
   gets(msg);
   CreateBiTree(root,msg);//创建二叉树

 //  display(root); 检验是否创建二叉树成功
//  printf("\n");
   levelTraversal2(root);
   return 0;
}//main

对应的运行结果:
在这里插入图片描述
在这里插入图片描述

标签:Node,arr,遍历,int,queue,之字形,二叉树,NULL,节点
来源: https://blog.csdn.net/weixin_46544385/article/details/116073753