數據結構實驗七 二叉鏈表


#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