這個作業屬於哪個課程 | https://edu.cnblogs.com/campus/qdu/DS2020 |
---|---|
這個作業要求在哪里 | https://edu.cnblogs.com/campus/qdu/DS2020/homework/11430 |
這個作業的目標 | <①掌握二叉樹的基本特性②掌握二叉樹的先序、中序、后序的遞歸遍歷算法③理解二叉樹的先序、中序、后序的非遞歸遍歷算法④通過求二叉樹的深度、葉子結點數和層序遍歷等算法,理解二叉樹的基本特性> |
學號 | 2018204167 |
一、實驗目的
1、掌握二叉樹的基本特性
2、掌握二叉樹的先序、中序、后序的遞歸遍歷算法
3、理解二叉樹的先序、中序、后序的非遞歸遍歷算法
4、通過求二叉樹的深度、葉子結點數和層序遍歷等算法,理解二叉樹的基本特性
二、實驗預習
說明以下概念
1、二叉樹:
二叉樹作為樹形結構的一種,是每個節點最多有兩個子樹的樹結構
2、遞歸遍歷:
所謂遍歷,是指沿着某條搜索路線,依次對樹(或圖)中每個節點均做一次訪問,遞歸作為遍歷的一種方法,是利用計算機的堆棧的概念,一般通過調用相同的函數來實現,函數中一般會設置終止的語句。
3、非遞歸遍歷:
樹的遍歷若采用非遞歸的方法,就要采用棧去模擬實現。
4、層序遍歷:
層序遍歷:進行層序遍歷時,對某一層的節點訪問完后,再按照他們的訪問次序對各個節點的左孩子和右孩子順序訪問,這樣一層一層進行,先訪問的節點其左右孩子也要先訪問,這正好符合隊列的操作特性。
三、實驗內容和要求
1、閱讀並運行下面程序,根據輸入寫出運行結果,並畫出二叉樹的形態。
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<conio.h>
#define MAX 20
typedef struct BTNode{ /*節點結構聲明*/
char data ; /*節點數據*/
struct BTNode *lchild;
struct BTNode *rchild ; /*指針*/
}*BiTree;
void createBiTree(BiTree *t){ /* 先序遍歷創建二叉樹*/
char s;
BiTree q;
printf("\nplease input data:(exit for #)");
s=getche();
if(s=='#'){*t=NULL; return;}
q=(BiTree)malloc(sizeof(struct BTNode));
if(q==NULL){printf("Memory alloc failure!"); exit(0);}
q->data=s;
*t=q;
createBiTree(&q->lchild); /*遞歸建立左子樹*/
createBiTree(&q->rchild); /*遞歸建立右子樹*/
}
void PreOrder(BiTree p){ /* 先序遍歷二叉樹*/
if ( p!= NULL ) {
printf("%c", p->data);
PreOrder( p->lchild ) ;
PreOrder( p->rchild) ;
}
}
void InOrder(BiTree p){ /* 中序遍歷二叉樹*/
if( p!= NULL ) {
InOrder( p->lchild ) ;
printf("%c", p->data);
InOrder( p->rchild) ;
}
}
void PostOrder(BiTree p){ /* 后序遍歷二叉樹*/
if ( p!= NULL ) {
PostOrder( p->lchild ) ;
PostOrder( p->rchild) ;
printf("%c", p->data);
}
}
void Preorder_n(BiTree p){ /*先序遍歷的非遞歸算法*/
BiTree stack[MAX],q;
int top=0,i;
for(i=0;i<MAX;i++) stack[i]=NULL;/*初始化棧*/
q=p;
while(q!=NULL){
printf("%c",q->data);
if(q->rchild!=NULL) stack[top++]=q->rchild;
if(q->lchild!=NULL) q=q->lchild;
else
if(top>0) q=stack[--top];
else q=NULL;
}
}
void release(BiTree t){ /*釋放二叉樹空間*/
if(t!=NULL){
release(t->lchild);
release(t->rchild);
free(t);
}
}
int main(){
BiTree t=NULL;
createBiTree(&t);
printf("\n\nPreOrder the tree is:");
PreOrder(t);
printf("\n\nInOrder the tree is:");
InOrder(t);
printf("\n\nPostOrder the tree is:");
PostOrder(t);
printf("\n\n先序遍歷序列(非遞歸):");
Preorder_n(t);
release(t);
return 0;
}
運行程序
輸入:
ABC##DE#G##F###
運行結果:
二叉樹形態:
2、在上題中補充求二叉樹中求結點總數算法(提示:可在某種遍歷過程中統計遍歷的結點數),並在主函數中補充相應的調用驗證正確性。
算法代碼:
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<conio.h>
#define MAX 20
typedef struct BTNode{ /*節點結構聲明*/
char data ; /*節點數據*/
struct BTNode *lchild;
struct BTNode *rchild ; /*指針*/
}*BiTree;
void createBiTree(BiTree *t){ /* 先序遍歷創建二叉樹*/
char s;
BiTree q;
printf("\nplease input data:(exit for #)");
s=getche();
if(s=='#'){*t=NULL; return;}
q=(BiTree)malloc(sizeof(struct BTNode));
if(q==NULL){printf("Memory alloc failure!"); exit(0);}
q->data=s;
*t=q;
createBiTree(&q->lchild); /*遞歸建立左子樹*/
createBiTree(&q->rchild); /*遞歸建立右子樹*/
}
void PreOrder(BiTree p){ /* 先序遍歷二叉樹*/
if ( p!= NULL ) {
printf("%c", p->data);
PreOrder( p->lchild ) ;
PreOrder( p->rchild) ;
}
}
void InOrder(BiTree p){ /* 中序遍歷二叉樹*/
if( p!= NULL ) {
InOrder( p->lchild ) ;
printf("%c", p->data);
InOrder( p->rchild) ;
}
}
void PostOrder(BiTree p){ /* 后序遍歷二叉樹*/
if ( p!= NULL ) {
PostOrder( p->lchild ) ;
PostOrder( p->rchild) ;
printf("%c", p->data);
}
}
void Preorder_n(BiTree p){ /*先序遍歷的非遞歸算法*/
BiTree stack[MAX],q;
int top=0,i;
for(i=0;i<MAX;i++) stack[i]=NULL;/*初始化棧*/
q=p;
while(q!=NULL){
printf("%c",q->data);
if(q->rchild!=NULL) stack[top++]=q->rchild;
if(q->lchild!=NULL) q=q->lchild;
else
if(top>0) q=stack[--top];
else q=NULL;
}
}
void release(BiTree t){ /*釋放二叉樹空間*/
if(t!=NULL){
release(t->lchild);
release(t->rchild);
free(t);
}
}
int PreOrder_num(BiTree p) {
int j=0;
BiTree stack[MAX],q;
int top=0,i;
for(i=0; i<MAX; i++) stack[i]=NULL; /*初始化棧*/
q=p;
while(q!=NULL) {
j++;
if(q->rchild!=NULL) stack[top++]=q->rchild;
if(q->lchild!=NULL) q=q->lchild;
else
if(top>0) q=stack[--top];
else q=NULL;
}
return j;
}
int main(){
BiTree t=NULL;
createBiTree(&t);
printf("\n\nPreOrder the tree is:");
PreOrder(t);
printf("\n\nInOrder the tree is:");
InOrder(t);
printf("\n\nPostOrder the tree is:");
PostOrder(t);
printf("\n\n先序遍歷序列(非遞歸):");
Preorder_n(t);
printf("\n\n結點總數:");
printf("%d",PreOrder_num(t));
release(t);
return 0;
}
運行結果:
3、在上題中補充求二叉樹中求葉子結點總數算法(提示:可在某種遍歷過程中統計遍歷的葉子結點數),並在主函數中補充相應的調用驗證正確性。
算法代碼:
int LeafNodes(BiTree p) {
int num1=0 ,num2=0;
if(p==NULL)
return 0;
else if(p->lchild==NULL&&p->rchild==NULL)
return 1;
else{
num1=LeafNodes(p->lchild) ;
num2=LeafNodes(p->rchild) ;
return (num1+num2);
}
}
int main(){
BiTree t=NULL;
createBiTree(&t);
printf("\n\nPreOrder the tree is:");
PreOrder(t);
printf("\n\nInOrder the tree is:");
InOrder(t);
printf("\n\nPostOrder the tree is:");
PostOrder(t);
printf("\n\n先序遍歷序列(非遞歸):");
Preorder_n(t);
printf("\n\n結點總數:");
printf("%d",PreOrder_num(t));
printf("\n\n葉結點總數:");
printf("%d",LeafNodes(t));
release(t);
return 0;
}
運行結果:
4、在上題中補充求二叉樹深度算法,並在主函數中補充相應的調用驗證正確性。
算法代碼:
int BTNodeDepth(BiTree p) {
int lchilddep,rchilddep;
if(p==NULL)
return 0;
else {
lchilddep=BTNodeDepth(p->lchild);
rchilddep=BTNodeDepth(p->rchild);
return(lchilddep>rchilddep)?(lchilddep+1):(rchilddep+1);
}
}
int main(){
BiTree t=NULL;
createBiTree(&t);
printf("\n\nPreOrder the tree is:");
PreOrder(t);
printf("\n\nInOrder the tree is:");
InOrder(t);
printf("\n\nPostOrder the tree is:");
PostOrder(t);
printf("\n\n先序遍歷序列(非遞歸):");
Preorder_n(t);
printf("\n\n結點總數:");
printf("%d",PreOrder_num(t));
printf("\n\n樹的深度:");
printf("%d",BTNodeDepth(t));
release(t);
return 0;
}
運行結果:
選做實驗:(代碼可另附紙)
4、補充二叉樹層次遍歷算法。(提示:利用隊列實現)
#include<stdio.h>
#include<stdlib.h>
typedef char EType;
struct BinaryTreeNode//定義節點
{
EType data;
struct BinaryTreeNode *LChild;
struct BinaryTreeNode *RChild;
};
typedef BinaryTreeNode BinaryTree;
typedef struct QType
{
BinaryTreeNode *ptr;
}QType;
typedef struct Queue
{
QType *element;//循環線性隊列,0號空間不使用(為了區分隊滿與隊空),注意這不是鏈式隊列
int front;
int rear;
int MaxSize;
}Queue;
void CreatBiTree(BinaryTreeNode **BT);
bool IsEmpty(Queue &Q);
bool IsFull(Queue &Q);
bool GetFront(Queue &Q,QType &result);
bool EnQueue(Queue &Q,QType &x);
bool DeQueue(Queue &Q,QType &result);
void LevelOrder_LtoR_UtoD(BinaryTreeNode *BT);
void LevelOrder_RtoL_UtoD(BinaryTreeNode *BT);
int main()
{
BinaryTreeNode *BT = NULL;
CreatBiTree(&BT);
printf("從上至下,從左至右遍歷二叉樹輸出為:");
LevelOrder_LtoR_UtoD(BT);
printf("\n");
printf("從上至下,從右至左遍歷二叉樹輸出為:");
LevelOrder_RtoL_UtoD(BT);
printf("\n");
return 0;
}
void CreatQueue(Queue &Q,int MaxQueueSize)
{
Q.MaxSize = MaxQueueSize;
Q.element = new QType[Q.MaxSize+1];
Q.front = 0;
Q.rear = 0;
}
bool IsEmpty(Queue &Q)
{
if(Q.front == Q.rear)
return true;
return false;
}
bool IsFull(Queue &Q)
{
if(Q.front == (Q.rear+1)%(Q.MaxSize+1))
return true;
return false;
}
bool GetFront(Queue &Q,QType &result)
{
if(IsEmpty(Q))
return false;
result = Q.element[(Q.front+1)%(Q.MaxSize+1)];
return true;
}
bool EnQueue(Queue &Q,QType &x)//進隊操作:rear向后移
{
if(IsFull(Q))
return false;
Q.rear = (Q.rear+1)%(Q.MaxSize+1);
Q.element[Q.rear] = x;
return true;
}
bool DeQueue(Queue &Q,QType &result)//出隊操作:front向后移
{
if(IsEmpty(Q))
return false;
Q.front = (Q.front+1)%(Q.MaxSize+1);
result = Q.element[Q.front];
return true;
}
void CreatBiTree(BinaryTreeNode **BT)//以前序遍歷方法輸入節點的data,構造一棵二叉樹
{
EType tem;
scanf("%c",&tem);
if(' ' == tem)
{
*BT = NULL;
}
else
{
*BT = new BinaryTreeNode;
(*BT)->data = tem;
CreatBiTree(&(*BT)->LChild);//遞歸
CreatBiTree(&(*BT)->RChild);
}
}
void LevelOrder_LtoR_UtoD(BinaryTreeNode *BT)
{
Queue Q;
QType temp;
BinaryTreeNode *p;
int MaxQueueSize = 50;
CreatQueue(Q,MaxQueueSize);
p = BT;
temp.ptr = p;
EnQueue(Q,temp);//先將根節點進隊
while(p)//二叉樹為空就直接結束
{
if(!DeQueue(Q,temp))//節點出隊,當隊空時,出隊操作返回false,程序return
return;//這是這個循環的出口,如果一個二叉樹不為空,最終都是在這里跳出循環
p = temp.ptr;
printf("%c\t",p->data);
if(p->LChild)//若該節點的左孩子存在,則將其進隊
{
temp.ptr = p->LChild;
EnQueue(Q,temp);
}
if(p->RChild)//若該節點的右孩子存在,則將其進隊
{
temp.ptr = p->RChild;
EnQueue(Q,temp);
}
}
}
void LevelOrder_RtoL_UtoD(BinaryTreeNode *BT)
{
Queue Q;
QType temp;
BinaryTreeNode *p;
int MaxQueueSize = 50;
CreatQueue(Q,MaxQueueSize);
p = BT;
temp.ptr = p;
EnQueue(Q,temp);
while(p)
{
if(!DeQueue(Q,temp))
return;
p = temp.ptr;
printf("%c\t",p->data);
if(p->RChild)
{
temp.ptr = p->RChild;
EnQueue(Q,temp);
}
if(p->LChild)
{
temp.ptr = p->LChild;
EnQueue(Q,temp);
}
}
}
5、補充二叉樹中序、后序非遞歸算法。
#include <iostream>
#include<string.h>
#include<stack>
#include<stdlib.h>
using namespace std;
typedef struct node
{
char data;
struct node *lchild,*rchild;
}BinTree;
typedef struct node1
{
BinTree *btnode;
bool isFirst;
}BTNode;
void creatBinTree(char *s,BinTree *&root) //創建二叉樹,s為形如A(B,C(D,E))形式的字符串
{
int i;
bool isRight=false;
stack<BinTree*> s1; //存放結點
stack<char> s2; //存放分隔符
BinTree *p,*temp;
root->data=s[0];
root->lchild=NULL;
root->rchild=NULL;
s1.push(root);
i=1;
while(i<strlen(s))
{
if(s[i]=='(')
{
s2.push(s[i]);
isRight=false;
}
else if(s[i]==',')
{
isRight=true;
}
else if(s[i]==')')
{
s1.pop();
s2.pop();
}
else if(isalpha(s[i]))
{
p=(BinTree *)malloc(sizeof(BinTree));
p->data=s[i];
p->lchild=NULL;
p->rchild=NULL;
temp=s1.top();
if(isRight==true)
{
temp->rchild=p;
cout<<temp->data<<"的右孩子是"<<s[i]<<endl;
}
else
{
temp->lchild=p;
cout<<temp->data<<"的左孩子是"<<s[i]<<endl;
}
if(s[i+1]=='(')
s1.push(p);
}
i++;
}
}
void display(BinTree *root) //顯示樹形結構
{
if(root!=NULL)
{
cout<<root->data;
if(root->lchild!=NULL)
{
cout<<'(';
display(root->lchild);
}
if(root->rchild!=NULL)
{
cout<<',';
display(root->rchild);
cout<<')';
}
}
}
void preOrder1(BinTree *root) //遞歸前序遍歷
{
if(root!=NULL)
{
cout<<root->data<<" ";
preOrder1(root->lchild);
preOrder1(root->rchild);
}
}
void inOrder1(BinTree *root) //遞歸中序遍歷
{
if(root!=NULL)
{
inOrder1(root->lchild);
cout<<root->data<<" ";
inOrder1(root->rchild);
}
}
void postOrder1(BinTree *root) //遞歸后序遍歷
{
if(root!=NULL)
{
postOrder1(root->lchild);
postOrder1(root->rchild);
cout<<root->data<<" ";
}
}
void preOrder2(BinTree *root) //非遞歸前序遍歷
{
stack<BinTree*> s;
BinTree *p=root;
while(p!=NULL||!s.empty())
{
while(p!=NULL)
{
cout<<p->data<<" ";
s.push(p);
p=p->lchild;
}
if(!s.empty())
{
p=s.top();
s.pop();
p=p->rchild;
}
}
}
void inOrder2(BinTree *root) //非遞歸中序遍歷
{
stack<BinTree*> s;
BinTree *p=root;
while(p!=NULL||!s.empty())
{
while(p!=NULL)
{
s.push(p);
p=p->lchild;
}
if(!s.empty())
{
p=s.top();
cout<<p->data<<" ";
s.pop();
p=p->rchild;
}
}
}
void postOrder2(BinTree *root) //非遞歸后序遍歷
{
stack<BTNode*> s;
BinTree *p=root;
BTNode *temp;
while(p!=NULL||!s.empty())
{
while(p!=NULL) //沿左子樹一直往下搜索,直至出現沒有左子樹的結點
{
BTNode *btn=(BTNode *)malloc(sizeof(BTNode));
btn->btnode=p;
btn->isFirst=true;
s.push(btn);
p=p->lchild;
}
if(!s.empty())
{
temp=s.top();
s.pop();
if(temp->isFirst==true) //表示是第一次出現在棧頂
{
temp->isFirst=false;
s.push(temp);
p=temp->btnode->rchild;
}
else //第二次出現在棧頂
{
cout<<temp->btnode->data<<" ";
p=NULL;
}
}
}
}
void postOrder3(BinTree *root) //非遞歸后序遍歷
{
stack<BinTree*> s;
BinTree *cur; //當前結點
BinTree *pre=NULL; //前一次訪問的結點
s.push(root);
while(!s.empty())
{
cur=s.top();
if((cur->lchild==NULL&&cur->rchild==NULL)||
(pre!=NULL&&(pre==cur->lchild||pre==cur->rchild)))
{
cout<<cur->data<<" "; //如果當前結點沒有孩子結點或者孩子節點都已被訪問過
s.pop();
pre=cur;
}
else
{
if(cur->rchild!=NULL)
s.push(cur->rchild);
if(cur->lchild!=NULL)
s.push(cur->lchild);
}
}
}
int main(int argc, char *argv[])
{
char s[100];
while(scanf("%s",s)==1)
{
BinTree *root=(BinTree *)malloc(sizeof(BinTree));
creatBinTree(s,root);
display(root);
cout<<endl;
preOrder2(root);
cout<<endl;
inOrder2(root);
cout<<endl;
postOrder2(root);
cout<<endl;
postOrder3(root);
cout<<endl;
}
return 0;
}
四、實驗小結
在這次實驗中,進一步學習到了二叉樹的先序、中序、后序的遞歸遍歷算法及非遞歸遍歷算法,通過求二叉樹的深度、葉子結點數和層序遍歷等算法,進一步理解了二叉樹的基本特性。