数据结构实验七 二叉链表


#include <iostream>
#include "stdio.h"
#include "stdlib.h"
#include "cstdlib"//syste()函数需要该头文件;

using namespace std;

#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;

typedef char TElemType;

//00 二叉链表存储结构的定义3个:数据元素---原子类型不用结构体,结点BiTNode---3个域的结构体二叉链表*BiTree---原子类型指针表示一棵二叉树,也可以表示某一结点指针
typedef struct BiTNode{
    TElemType data;
    struct BiTNode *lchild, *rchild;
  }BiTNode,*BiTree;
//typedef BiTree SElemType; 
//定义一个栈,用于完成二叉树遍历的非递归算法
typedef struct StackNode
    BiTNode *data; //存放的是二叉树(指针型)结点,也就是将一个树结点类型的指针作为数据放入栈结点的data中
    struct StackNode *next;//指向下一个栈结点(data,next)
  }StackNode, *LinkStack;

//定义一个队列,用于完成二叉树层次遍历的非递归算法
typedef struct QNode { // 结点QNode定义
    BiTNode *data;
    struct QNode *next;
  }QNode, *QueuePtr;

typedef struct { // 链队LinkQueue定义
    QueuePtr front; // 队头指针
    QueuePtr rear; // 队尾指针
  } LinkQueue;

//01 _按先序遍历创建,没有头结点、长度,也无需提前开辟空间放数据(开辟一个空间放一个数据),所以不用初始化了T.data=0;t.lchile=null,t.lchild=null;
Status CreateBiTree(BiTree &T)

  char ch;

  cin>>ch; //上机输入_P126最下面 ABC##DE#G##F###,注意画图就知道怎么回事了C##表示C为根,左右孩子为空。ABD##E##CF##G##什么图?

if(ch=='#')
{
  T=NULL;
}
else
{
  T=new BiTNode;
  if (!T)
  {
    exit(OVERFLOW);
  }
  T->data=ch;//先序
  CreateBiTree(T->lchild);//左子树递归创建
  CreateBiTree(T->rchild);//右子树递归创建  根左右
}
return OK;

}

  //02 _先序遍历

Status PreOrderTraverse(BiTree T)

{
  if(T)
  {
    cout<<T->data<<" ";
    PreOrderTraverse(T->lchild);
    PreOrderTraverse(T->rchild);
  }
  return OK;
}

//03 中序遍历
Status InOrderTraverse(BiTree T)
{
  if(T)
  {
    InOrderTraverse(T->lchild);
    cout<<T->data<<" ";
    InOrderTraverse(T->rchild);
  }
  return OK;
}

//04 后序遍历
Status PostOrderTraverse(BiTree T)
{
  if(T)
  {
    PostOrderTraverse(T->lchild);
    PostOrderTraverse(T->rchild);
    cout<<T->data<<" ";
  }
  return OK;
}

 //05 复制二叉树,先序方式

Status CopyBiTree(BiTree T,BiTree &NewT)//加&
{
  if(T==NULL)
  {
    NewT=NULL;
  }
  else
  {
    NewT=new BiTNode;
    NewT->data=T->data;
    CopyBiTree(T->lchild,NewT->lchild);
    CopyBiTree(T->rchild,NewT->rchild);
  }
  return OK;
}

//06 计算二叉树的深度
int GetDeptbBT(BiTree T)//深度不能用status了
{
  int m,n;
  if(T==NULL)
    return 0;
  else
  {
    m=GetDeptbBT(T->lchild);//左子树也是一棵二叉树,所以可以递归调用函数自身
    n=GetDeptbBT(T->rchild); //同上
    if(m>n)
      return (m+1);
    else
      return (n+1);
  }
}

//07 计算结点个数
int NodeCount(BiTree T)
{
  if(T==NULL)
    return 0;
  else
    return NodeCount(T->lchild)+NodeCount(T->rchild)+1;//根节点也要加上,故+1;
} 

//08 计算叶子结点个数
int LeafNodeCount(BiTree T)
{
  if(T==NULL)
    return 0;
  else
    if(T->lchild==NULL&&T->rchild==NULL)//碰见叶子结点终止递归
      return 1;
    else
      return LeafNodeCount(T->lchild)+LeafNodeCount(T->rchild);//注意树若只有一个根节点,叶子结点数是1
}

//09 中序非递归遍历

Status InitStack(LinkStack &S)

{
  S = NULL; //由于链栈没有头结点,所以不用开辟一个结点空间,也不用定义S->next = NULL;甚至不需要初始化子函数,只需主函数中声明时StackNode *S=NULL即可;
  return OK;
}
int StackEmpty(LinkStack S)
{
  if (S == NULL)
    return 1; //链栈为空,用0还是用1表示,看你后面程序中怎么用,用1方便就置1;
  else
    return 0;
}
Status Push(LinkStack &S, BiTree tNode)//每入栈一个结点,总要修改指针S的值,使其指向新的栈顶元素,故加&;
{
  StackNode *sNode;//等价于LinkStack p;为什么用StackNode *p?而不用LinkStack p呢???
  sNode = (LinkStack)malloc(sizeof(StackNode));
  sNode->data = tNode;
  sNode->next = S; //新入栈的元素的指针指向原本的栈顶元素
  S = sNode;//重新让S指向刚入栈的(栈顶)元素
  return OK;
}
Status Pop(LinkStack &S, BiTree &tNode)
{
  StackNode *sNode;//栈s顶结点
  if (S == NULL)//链栈为空就不能出栈
  {
    cout<<"栈空,无法读取!";
    return ERROR;
  }
  tNode = S->data;//S就是栈顶结点
  sNode = S;
  S = S->next; //出栈时,指针要指向栈顶的下一个元素了
  free(sNode); //释放栈顶指针
  return OK;
} 

Status InOrderTraverseByIteration(BiTree T)//二叉树遍历的中序非递归算法,先序后序呢???

{

  BiTNode *p; //等价于BiTree p;     为什么不用BiTree p呢???主要是BiTree用来表示二叉树,给人的感觉 p 是一个二叉树,但实际p只是一个节点指针
  p = T; //T表示一棵链树,也表示链树的根结点,把根赋值给p
  StackNode *S;
  InitStack(S);//初始化栈,置空
  while (p!= NULL || !StackEmpty(S))
  {
    if (p != NULL)

    {
      Push(S,p); //把根节点压栈
      p = p->lchild; //先去遍历左子树
    }
    else

    {
      Pop(S, p); //一开始p是二叉树最左结点,相当于其是一个左孩子是空的根节点,再遍历右子树,如果右孩子也空,说明其是一个叶子结点
      cout<<p->data<<" ";
      p = p->rchild; //遍历右子树
    }
  }
  return OK;
} 

 //10 二叉树先序非递归遍历

Status PreOrderTraverseByIteration(BiTree T)
{
  BiTNode *p; //等价于BiTree p;为什么用BiTNode *p?而不用BiTree p呢???
  p = T; //T表示一棵链树,也表示链树的根结点,把根赋值给p
  StackNode *S;
  InitStack(S);//空栈置空
  while (p!= NULL || !StackEmpty(S))
  {
    if (p != NULL)

    {
      cout<<p->data<<" ";//先访问根
      Push(S,p->rchild);//和下一行代码顺序不能换,右孩子入栈,先不访问 //若此处Push(S,p);则else要在pop后面+p=p->rchild;
      p = p->lchild;//沿着左孩子一直进行,访问左孩子,后面出栈访问右孩子,根 左  右
    }
    else

    {
      Pop(S, p);
    }
  }
  return OK;
}

//11 二叉树后序非递归遍历

 Status PostOrderTraverseByIteration(BiTree T)//二叉树后序遍历

{
  BiTNode *p,*pre = NULL; //p为当前;pre为上次访问结点
  StackNode *S;

  InitStack(S);//空栈置空

  if(T==NULL)
    return OK;

  Push(S,T);//根先入栈 最后访问 左 右 根 

  while (!StackEmpty(S))
  {
    GetTop(S,p);//先读取,不是出栈Pop,满足下面的判断在出栈,意思是判断结点是叶子结点,或 不是叶子结点但被访问过 那就输出p->data并将p出栈,用pre记录p;
    if ((p->lchild==NULL&&p->rchild==NULL)||(pre!=NULL&&(pre==p->lchild||pre==p->rchild)))//如果当前结点没有孩子结点或者孩子节点都已被访问过
    {
      cout<<p->data<<" ";
      Pop(S,p);//把当前访问过的元素出栈
      pre=p;//pre记录这个刚被访问出栈的元素
    }
    else
    {
      if(p->rchild!=NULL)
        Push(S,p->rchild);//根循坏外已入栈,现在先压右,在压左,出栈是 左 右 根,便是 后序
      if(p->lchild!=NULL)
        Push(S,p->lchild);
    }
  }
  return OK;
} 

  

  

  

  //12 二叉树层序遍历,用到队列了。 

Status InitLQueue(LinkQueue &Q)
{
  Q.rear=Q.front = new QNode;
  Q.front->next = NULL;//头结点需要修改next为NULL;
  return OK;
}
Status EnQueue(LinkQueue &Q, BiTree tNode)//入队,仅需将原来的e改成BiTree tNode即可,我们要入栈一个二叉链结点,其实定义中可以定义一个指针* BiTreeNode,字面意思更合理,是Node了,不是Bitree了
{
  QueuePtr p;
  if(Q.front)
  {
    p = new QNode;
    p->data = tNode;
    p->next = NULL;
    Q.rear->next = p;
    Q.rear = p;
    return OK;
  }
  else
  {
    cout << "链队不存在!" <<endl;
    return ERROR;
  }
}
Status DeQueue(LinkQueue &Q, BiTree &tNode)//出队
{
  if (Q.front == Q.rear)
  {
    printf("空链队\n");
    return ERROR;
  }
  QueuePtr p;
  p = Q.front->next; //p指向队头元素
  tNode = p->data;
  Q.front->next = p->next; //头结点的next指向p-next(p的后继结点);
  if (Q.rear == p)
  {
    Q.rear = Q.front; //如果只有一个结点,尾指针也要修改;
  }
  delete p;
  return OK;
}
Status GetLQHead(LinkQueue Q, BiTree &tNode)//Q没变不加,tNode需要返回给主函数故加&。//其实本程序没用到这个子函数不用写上
{
  if(Q.front != Q.rear)
  {
    tNode = Q.front->next->data;
    return OK;
  }
  else
  {
    printf("当前链队为空,无头元素!\n");
    return ERROR;
  }
}
Status LQisEmpty(LinkQueue Q)
{
  if (Q.front == Q.rear)
    return 1;
  else
    return 0;
}

Status BreadthFirstOrder(BiTree T)//层序遍历
{
  if(T==NULL)
    return OK;
  BiTNode *p=T;
  LinkQueue Q;
  InitLQueue(Q);
  EnQueue(Q, p);//根结点先入队

  while(!LQisEmpty(Q))
  {
    DeQueue(Q, p);
    cout<<p->data<<" ";//出队一个,后面要把左右孩子入队,如果空了不入队,一直出队直至空队
    if(p->lchild!=NULL)
      EnQueue(Q, p->lchild);
    if(p->rchild!=NULL)
      EnQueue(Q, p->rchild);
  }
  return OK;
}

 

 

 

 void main()

{
  BiTree BT;

  char ch;

 do{

  system("cls");
  printf("创建树,输入0为空树:\n");
  CreateBiTree(BT);
  cout<<"先序遍历BT: ";
  PreOrderTraverse(BT);
  cout<<"\n先序迭代BT: ";
  PreOrderTraverseByIteration(BT); 

  cout<<"\n中序遍历BT: ";
  InOrderTraverse(BT);
  cout<<"\n中序迭代BT: ";
  InOrderTraverseByIteration(BT); 

  cout<<"\n后序遍历BT: ";
  PostOrderTraverse(BT);

cout<<"\n后序迭代BT: ";

PostOrderTraverseByIteration(BT); 

     BiTree NT;

  CopyBiTree(BT,NT);

  cout<<"\n先序复制BT: ";
  PreOrderTraverse(NT);

  cout<<"\n层序遍历BT: ";

  BreadthFirstOrder(BT);

  cout<<"\n二叉深度: "<<GetDeptbBT(BT);

  cout<<"\n结点个数: "<<NodeCount(BT);

  cout<<"\n叶子个数: "<<LeafNodeCount(BT); 

     cout<<"\n是否继续测试(输入y或Y继续,任意其他键结束):";

  cin>>ch; 

 }while(ch == 'y' || ch == 'Y');  

  system("pause");
}

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM