二叉树的编程模型2



#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define OK 0
#define ERROR -1


/************************************

  创建树

 ************************************/

//按先后次序输入二叉树中结点的值(一个字符),#表示空树
//构造二叉链表表示的二叉树
//方法一

typedef struct _tree
{
 char data;
 struct _tree *lchild;
 struct _tree *rchild;

} *BiTree,
 BiTNode;


BiTree CreateTree(BiTree t)
{
 char ch;
 scanf("%c", &ch);

 if(ch == '#')
 {
  t = NULL;
 }
 else
 {
  t = (BitNode *)malloc(sizeof(BitNode));
  if(t == NULL)
  {
   fprintf(stderr, "malloc() error in CreateTree.\n");
   return;
  }

  t->data = ch;                        //生成根结点
  t->lchild = CreateTree(t->lchild);    //构造左子树
  t->rchild = CreateTree(t->rchild);    //构造右子树
 }
 return t;
}

//创建树方法二
int CreateTree2(BiTree *t)
{
 char ch;
 scanf("%c", &ch);

 if(ch == '#')
 {
  (*t) = NULL;
 }
 else
 {
  (*t) = (BiTree)malloc(sizeof(BitNode));
  if((*t) == NULL)
  {
   fprintf(stderr, "malloc() error in CreateTree2.\n");
   return ERROR;
  }

  (*t)->data = ch;
  CreateTree2(&((*t)->lchild));
  CreateTree2(&((*t)->rchild));
 }
 return OK;
}


/*************************************

  二叉树的深度遍历:
  前序遍历二叉树
  中序遍历二叉树
  后序遍历二叉树三种

  二叉树的广度遍历:
  层次遍历

 ***************************************/


/*********************************

  前序递归遍历

 *********************************/

/*
   规则是若二叉树为空,则空操作返回,否则先访问根结点,
   然后前序遍历左子树,再前序遍历右子树
*/

//前序递归遍历
void PreOrderTraverse(BiTree t)
{
 if(t != NULL)
 {
  printf("%c ", t->data);
  PreOrderTraverse(t->lchild);
  PreOrderTraverse(t->rchild);
 }
}


/*********************************

  前序非递归遍历

 **********************************/


/*
   根据前序遍历访问的顺序,优先访问根结点,然后再分别访问左孩子和右孩子。
   即对任一结点,其可看做是根结点,因此可以直接访问,访问完之后,若其左孩子不为空,
   按相同的规则访问它的左子树;当访问其左子树时,再访问它的右子树,因此其处理过程如下:

   对于任一结点p:

   a. 访问结点p,并将结点p入栈;
   b. 判断结点p的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点p,循环置a;若不为空,则将p的左孩子置为当前结点p;
   c. 直到p为空,并且栈为空,则遍历结束。
*/

//前序非递归遍历
int NoPreOrderTraverse(BiTree t)
{
 SqStack s;
 InitStack(&s);

 BiTree tmp = t;
 if(tmp == NULL)
 {
  fprintf(stdout, "the tree is null.\n");
  return ERROR;
 }

 while((tmp != NULL) || (IsEmpty(&s) != 1))
 {
  while(tmp != NULL)
  {
   Push(&s, tmp);
   printf("%c ", tmp->data);
   tmp = tmp->lchild;
  }
  if(IsEmpty(&s) != 1)
  {
   Pop(&s, &tmp);
   tmp = tmp->rchild;
  }
 }

 return OK;
}

/************************

  中序递归遍历

 ************************/


/*
   规则是若树为空,则空操作返回,否则从根结点开始(注意这里并不是先访问根结点),
   中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树
*/

//中序递归遍历
void InOrderTraverse(BiTree t)
{
 if(t != NULL)
 {
  InOrderTraverse(t->lchild);
  printf("%c ", t->data);
  InOrderTraverse(t->rchild);
 }
}

/******************************

  中序非递归遍历

 ******************************/

/*
   根据中序遍历的顺序,对于任一结点,
   优先访问其左孩子,而左孩子结点又可以看做一个根结点,
   然后继续访问其左孩子结点,直到遇到左孩子结点为空的结点才停止访问,
   然后按相同的规则访问其右子树。其处理过程如下:

   对于任一结点:

   a. 若其左孩子不为空,则将p入栈,并将p的左孩子设置为当前的p,然后对当前结点再进行相同的操作;

   b. 若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的p置为栈顶结点的右孩子;

   c. 直到p为空并且栈为空,则遍历结束。
*/


//中序非递归遍历二叉树
int NoInOrderTraverse(BiTree t)
{
 SqStack s;
 InitStack(&s);

 BiTree tmp = t;
 if(tmp == NULL)
 {
  fprintf(stderr, "the tree is null.\n");
  return ERROR;
 }

 while(tmp != NULL || (IsEmpty(&s) != 1))
 {
  while(tmp != NULL)
  {
   Push(&s, tmp);
   tmp = tmp->lchild;
  }

  if(IsEmpty(&s) != 1)
  {
   Pop(&s, &tmp);
   printf("%c ", tmp->data);
   tmp = tmp->rchild;
  }
 }
 return OK;
}


/**********************

  后序递归遍历

 **********************/

//规则是若树为空,则空操作返回,
//否则从左到右先叶子后结点的方式遍历访问左右子树,
//最后是访问根结点

//后序递归遍历
void PostOrderTraverse(BiTree t)
{
 if(t != NULL)
 {
  PostOrderTraverse(t->lchild);
  PostOrderTraverse(t->rchild);
  printf("%c ", t->data);
 }
}


/****************************
  后序非递归遍历
 ****************************/

/*
   后序遍历的非递归实现是三种遍历方式中最难的一种。
   因为在后序遍历中,要保证左孩子和右孩子都已被访问,
   并且左孩子在右孩子之前访问才能访问根结点,
   这就为流程控制带来了难题。下面介绍一种思路。

   要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点p,先将其入栈。
   若p不存在左孩子和右孩子,则可以直接访问它,或者p存在左孩子或右孩子,
   但是其左孩子和右孩子都已经被访问过了,则同样可以直接访问该结点。
   若非上述两种情况,则将p的右孩子和左孩子依次入栈,
   这样就保证了每次取栈顶元素的时候,左孩子在右孩子之前别访问,
   左孩子和右孩子都在根结点前面被访问。

*/

//后序非递归遍历二叉树
int NoPostOrderTraverse(BiTree t)
{
 SqStack s;
 InitStack(&s);

 BiTree cur;     //当前结点 
 BiTree pre = NULL;      //前一次访问的结点
 BiTree tmp;

 if(t == NULL)
 {
  fprintf(stderr, "the tree is null.\n");
  return ERROR;
 }

 Push(&s, t);
 while(IsEmpty(&s) != 1)
 {
  GetTop(&s, &cur);//
  if((cur->lchild == NULL && cur->rchild == NULL) || (pre != NULL && (pre == cur->lchild || pre == cur->rchild)))
  {
   printf("%c ", cur->data);    //如果当前结点没有孩子结点或者孩子结点都已被访问过
   Pop(&s, &tmp);
   pre = cur;
  }
  else
  {
   if(cur->rchild != NULL)
   {
    Push(&s, cur->rchild);
   }
   if(cur->lchild != NULL)
   {
    Push(&s, cur->lchild);
   }
  }
 }
 return OK;
}

/**************************

  二叉树的广度遍历
 **************************/


/*
   广度遍历二叉树(即层次遍历)是用队列来实现的,从二叉树的第一层(根结点)开始,
   自上而下逐层遍历;在同一层中,按照从左到右的顺序对结点逐一访问

   按照从根结点到叶结点、从左子树到右子树的次序访问二叉树的结点,具体思路如下:

   A. 初始化一个队列,并把根结点入队列;

   B. 当队列为非空时,循环执行步骤3到步骤5,否则执行步骤6;

   C. 出队列取得一个结点,访问该结点;

   D. 若该结点的左子树为非空,则将该结点的左子树入队列;

   E. 若该结点的右子树为非空,则将该结点的右子树入队列;

   F. 结束。

*/

//层次遍历二叉树 - 广度遍历二叉树 - 队列
int TraverseBiTree(BiTree t)
{
 LinkQueue q;
 InitQueue(&q);

 BiTree tmp = t;
 if(tmp == NULL)
 {
  fprintf(stderr, "the tree is null.\n");
  return ERROR;
 }

 InsertQueue(&q, tmp);
 while(QueueIsEmpty(&q) != OK)
 {
  DeQueue(&q, &tmp);
  printf("%c ", tmp->data);
  if(tmp->lchild != NULL)
  {
   InsertQueue(&q, tmp->lchild);
  }
  if(tmp->rchild != NULL)
  {
   InsertQueue(&q, tmp->rchild);
  }
 }

 return OK;
}



/************************************

  求树的深度

 ************************************/

/*

   树中结点的最大层次称为树的深度。
   对于二叉树,求解树的深度用以下两种方法实现。即非递归和递归的方法实现。

*/

//二叉树的深度 - 递归
//返回值: 二叉树的深度
int BiTreeDeep(BiTree t)
{
 int dept = 0;

 if(t)
 {
  int lchilddept = BiTreeDeep(t->lchild);
  int rchilddept = BiTreeDeep(t->rchild);

  dept = lchilddept >= rchilddept ? (lchilddept + 1) : (rchilddept + 1);
 }

 return dept;
}


/*

   对于非递归求解二叉树的深度,这里采用了层次遍历的原理,通过层次遍历,找到二叉树的最后一个结点。
   然后,根据该结点,寻找其双亲结点,即找到其上一层,此时深度dept加1,依次进行,直到根结点为止。

*/

//返回二叉树的深度 - 非递归  - 受层次遍历二叉树的影响
//返回值: 二叉树的深度
int NoBiTreeDeep(BiTree t)
{
 LinkQueue q;
 InitQueue(&q);

 BiTree tmp = t;
 if(tmp == NULL)
 {
  return ERROR;
 }

 InsertQueue(&q, tmp);
 while(QueueIsEmpty(&q) != OK)
 {
  DeQueue(&q, &tmp);
  //printf("%c ", tmp->data);

  if(tmp->lchild != NULL)
  {
   InsertQueue(&q, tmp->lchild);
  }
  if(tmp->rchild != NULL)
  {
   InsertQueue(&q, tmp->rchild);
  }
 }

 int deep = 0;
 BiTree m = tmp;
 BiTree n = t;
 while(m != n)
 {
  InsertQueue(&q, n);
  while(QueueIsEmpty(&q) != OK)
  {
   DeQueue(&q, &tmp);
   if(m == tmp->lchild || m == tmp->rchild)
   {
    deep++;
    m = tmp;
    break;
   }

   if(tmp->lchild != NULL)
   {
    InsertQueue(&q, tmp->lchild);
   }
   if(tmp->rchild != NULL)
   {
    InsertQueue(&q, tmp->rchild);
   }
  }
 }

 return deep + 1;    //深度从1开始
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值