二叉樹的基本應用(PTA題型為例)
我們根據PTA上面的題對於二叉樹有一個更深的認識
7-1 二叉樹的建立和遞迴遍歷 (20 分)從鍵盤接受輸入擴充套件先序序列,以二叉連結串列作為儲存結構,建立二叉樹。並輸出這棵二叉樹的先序、中序和後序遍歷序列。 二叉樹結點的data是字元型別資料, 其中#表示空格字元。
輸入格式:
輸入擴充套件先序序列。二叉樹結點的data是字元型別資料, 其中#表示空格字元。
輸出格式:
第一行輸出先序遍歷序列;第二行輸出中序遍歷序列;第三行輸出後序遍歷序列。
輸入樣例:
ABC##DE#G##F###
輸出樣例:
ABCDEGF
CBEGDFA
CGEFDBA
//解析:簡單的遞迴實現遍歷
//主要是使用擴充套件先序序列建立二叉樹,本題不過多分析
#include<stdio.h> #include7-2 非遞迴先序和中序遍歷 (20 分)<stdlib.h> typedef struct node{ char data; struct node *Lchild; struct node *Rchild; }BiNode, *BiTree; //擴充套件先序建立二叉樹 BiTree Creat_Pre_Bitree(BiTree root) { char c; c = getchar(); if(c=='#') return NULL; else{ root = (BiTree)malloc(sizeof(BiNode)); root->data = c; root->Lchild = Creat_Pre_Bitree(root->Lchild); root->Rchild = Creat_Pre_Bitree(root->Rchild); } return root; } //先序遍歷 void PreOrder(BiTree root) { if(root) { printf("%c", root->data); PreOrder(root->Lchild); PreOrder(root->Rchild); } } //中序遍歷 void InOrder(BiTree root) { if(root){ InOrder(root->Lchild); printf("%c", root->data); InOrder(root->Rchild); } } //後序遍歷 void PostOrder(BiTree root) { if(root){ PostOrder(root->Lchild); PostOrder(root->Rchild); printf("%c", root->data); } } int main() { BiTree root = Creat_Pre_Bitree(&root); PreOrder(root); printf("\n"); InOrder(root); printf("\n"); PostOrder(root); printf("\n"); return 0; }
從鍵盤接收擴充套件先序序列,以二叉連結串列作為儲存結構,建立二叉樹。採取非遞迴方法輸出這棵二叉樹的先序、中序遍歷序列。
輸入格式:
輸入擴充套件先序序列。二叉樹結點的data是字元型別資料, 其中#表示空格字元。
輸出格式:
第一行輸出先序遍歷序列,第二行輸出中序遍歷序列。
輸入樣例:
ABC##DE#G##F###
輸出樣例:
ABCDEGF
CBEGDFA
//本題主要考察非遞迴的先序和中序遍歷,
演算法實現:大多數的遞迴問題的非遞迴演算法中都需要用棧來消除遞迴
先序遍歷的實現:
1)訪問root,root入棧並進入其左子樹,進而訪問左子樹的root併入棧,在進入下一層左子樹,...直到為空
2)棧非空則從棧頂退出上一層的結點,並進入該節點的右子樹
//棧的基本操作見:
#include<stdio.h> #include<stdlib.h> #define MaxSize 100 typedef struct node{ char data; struct node *Lchild; struct node *Rchild; }BiNode, *BiTree; //擴充套件先序建立二叉樹 void Creat_Pre_Bitree(BiTree *root) { char c; c = getchar(); if(c=='#') *root = NULL; else { *root = (BiTree)malloc(sizeof(BiNode)); (*root)->data = c; Creat_Pre_Bitree(&((*root)->Lchild)); Creat_Pre_Bitree(&((*root)->Rchild)); } } /*非遞迴*/ //棧相關 typedef struct { BiTree stdata[MaxSize]; int top; } sqstack; //建立棧 sqstack* Init_Stack() { sqstack *s; s = (sqstack *)malloc(sizeof(sqstack)); s->top = -1; return s; } //判空 int IsEmpty(sqstack *s) { if(s->top==-1) return 1; else return 0; } //入棧 void Push(sqstack*s,BiTree root){ if(s->top<MaxSize){ s->top++; s->stdata[s->top] = root; } return; } //出棧 int Pop(sqstack*s,BiTree *x){ if(IsEmpty(s)) { return 0; } else { *x = s->stdata[s->top]; s->top--; return 1; } } //非遞迴先序 void LPreOrder(BiTree root) { sqstack* s; s = Init_Stack(); BiTree p; p = root; while(p!=NULL||!IsEmpty(s)){ while(p!=NULL){ printf("%c", p->data); Push(s, p); p = p->Lchild; } if(!IsEmpty(s)){ Pop(s,&p); p = p->Rchild; } } } //非遞迴中序 void LInOrder(BiTree root) { sqstack* s; s = Init_Stack(); BiTree p = root; while(p!=NULL||!IsEmpty(s)){ while(p!=NULL){ Push(s, p); p = p->Lchild; } if(!IsEmpty(s)){ Pop(s,&p); printf("%c", p->data); p = p->Rchild; } } } int main() { BiTree root; Creat_Pre_Bitree(&root); LPreOrder(root); printf("\n"); LInOrder(root); printf("\n"); return 0; }7-3 非遞迴後序遍歷 (15 分)
從鍵盤接收擴充套件先序序列,以二叉連結串列作為儲存結構,建立二叉樹。採取非遞迴方法輸出這棵二叉樹的後序遍歷序列。
輸入格式:
輸入擴充套件先序序列。二叉樹結點的data是字元型別資料, 其中#表示空格字元。
輸出格式:
輸出後序遍歷序列。
輸入樣例:
ABC##DE#G##F###
輸出樣例:
CGEFDBA
//後序非遞迴不能使用如上先、中序的遍歷方法是因為,當訪問完左右子樹在訪問結點後子樹返回時無法有效的判斷它到底是左子樹返回的還是右子樹返回的,此時我們就需要再加一個判斷
/*演算法實現*/
1.當前結點進棧,並進入其左子樹,重複至當前結點為空;
2.若棧非空,判斷棧頂結點P的右子樹是否為空,右子樹是否訪問過,是則退棧,訪問p結點,p賦給q,p置空;不是,則進入p的右子樹。
#include<stdio.h> #include<stdlib.h> #define MaxSize 100 typedef struct node{ char data; struct node *Lchild; struct node *Rchild; }BiNode, *BiTree; //擴充套件先序建立二叉樹 void Creat_Pre_Bitree(BiTree *root) { char c; c = getchar(); if(c=='#') *root = NULL; else { *root = (BiTree)malloc(sizeof(BiNode)); (*root)->data = c; Creat_Pre_Bitree(&((*root)->Lchild)); Creat_Pre_Bitree(&((*root)->Rchild)); } } /* //遞迴 void PreOrder(BiTree root) { if(root) { printf("%c", root->data); PreOrder(root->Lchild); PreOrder(root->Rchild); } } void InOrder(BiTree root) { if(root){ InOrder(root->Lchild); printf("%c", root->data); InOrder(root->Rchild); } } void PostOrder(BiTree root) { if(root){ PostOrder(root->Lchild); PostOrder(root->Rchild); printf("%c", root->data); } } */ /*非遞迴*/ //棧相關 typedef struct { BiTree stdata[MaxSize]; int top; } sqstack; //建立棧 sqstack* Init_Stack() { sqstack *s; s = (sqstack *)malloc(sizeof(sqstack)); s->top = -1; return s; } //判空 int IsEmpty(sqstack *s) { if(s->top==-1) return 1; else return 0; } //入棧 void Push(sqstack*s,BiTree root){ if(s->top<MaxSize){ s->top++; s->stdata[s->top] = root; } return; } //出棧 int Pop(sqstack*s,BiTree *x){ if(IsEmpty(s)) { return 0; } else { *x = s->stdata[s->top]; s->top--; return 1; } } //取棧頂元素 void Top(sqstack*s,BiTree*p) { if(IsEmpty(s)) return; else *p = s->stdata[s->top]; } //非遞迴先序 void LPreOrder(BiTree root) { sqstack* s; s = Init_Stack(); BiTree p; p = root; while(p!=NULL||!IsEmpty(s)){ while(p!=NULL){ printf("%c", p->data); Push(s, p); p = p->Lchild; } if(!IsEmpty(s)){ Pop(s,&p); p = p->Rchild; } } } //非遞迴中序 void LInOrder(BiTree root) { sqstack* s; s = Init_Stack(); BiTree p = root; while(p!=NULL||!IsEmpty(s)){ while(p!=NULL){ Push(s, p); p = p->Lchild; } if(!IsEmpty(s)){ Pop(s,&p); printf("%c", p->data); p = p->Rchild; } } } //非遞迴後續遍歷 void LPostOrder(BiTree root) { sqstack *s; BiTree p, q; s=Init_Stack(); p = root; q = NULL; while(p!=NULL||!IsEmpty(s)) { while(p!=NULL) { Push(s, p); p = p->Lchild; } if(!IsEmpty(s)) { Top(s, &p); if((p->Rchild==NULL)||(p->Rchild==q)) { Pop(s, &p); printf("%c", p->data); q = p; p = NULL; } else p = p->Rchild; } } } int main() { BiTree root; Creat_Pre_Bitree(&root); LPostOrder(root); return 0; }
7-4 層次遍歷 (10 分)
從鍵盤接收擴充套件先序序列,以二叉連結串列作為儲存結構,建立二叉樹。輸出這棵二叉樹的層次遍歷序列。
輸入格式:
輸入擴充套件先序序列。二叉樹結點的data是字元型別資料, 其中#表示空格字元。
輸出格式:
輸出層次遍歷序列。
輸入樣例:
ABC##DE#G##F###
輸出樣例:
ABCDEFG
//二叉樹的層次遍歷
所謂二叉樹的層次遍歷就是指從二叉樹的第一層(root結點)開始,自上而下逐層遍歷,同層從左到右逐個訪問。由此我們可知這一訪問過程的特點是:先訪問的結點其孩子也將先訪問,後訪問的結點其孩子也後訪問,
這與佇列的·操作吻合,因此我們用佇列進行節點訪問次序的控制
//演算法實現
1.隊頭結點出隊,並訪問出隊節點
2.出隊結點的非空左、右孩子依次入隊
#include<stdio.h> #include<stdlib.h> #define MaxSize 100 typedef struct node{ char data; struct node *Lchild; struct node *Rchild; }BiNode, *BiTree; //擴充套件先序建立二叉樹 void Creat_Pre_Bitree(BiTree *root) { char c; c = getchar(); if(c=='#') *root = NULL; else { *root = (BiTree)malloc(sizeof(BiNode)); (*root)->data = c; Creat_Pre_Bitree(&((*root)->Lchild)); Creat_Pre_Bitree(&((*root)->Rchild)); } } /*queue*/ //迴圈佇列 typedef struct { BiTree data[MaxSize]; int front, rear; }Csqueue; //置空隊 初始化 Csqueue* Init_Queue() { Csqueue *q; q = (Csqueue *)malloc(sizeof(Csqueue)); q->front = q->rear = MaxSize - 1; return q; } //入隊 int EnterQueue(Csqueue*q,BiTree x) { if((q->rear+1)%MaxSize==q->front) { //printf("FULL"); return 0; } else { q->rear = (q->rear + 1) % MaxSize; q->data[q->rear] = x; return 1; } } //出隊 int DelectQueue(Csqueue*q,BiTree*x) { if(q->rear==q->front) { //printf("NUll"); return 0; } else { q->front = (q->front + 1) % MaxSize; *x = q->data[q->front]; return 1; } } //判空 int isempty(Csqueue*q) { if(q->front==q->rear) return 1; return 0; } void levelorder(BiTree root) { Csqueue *q; BiTree p; q = Init_Queue(); EnterQueue(q, root); while(!isempty(q)) { DelectQueue(q, &p); printf("%c", root->data); if (p->Lchild != NULL) EnterQueue(q, p->Lchild); if(p->Rchild!=NULL) EnterQueue(q, p->Rchild); } } int main() { BiTree root; Creat_Pre_Bitree(&root); levelorder(root); return 0; }
7-5 結點個數 (20 分)
從鍵盤接收擴充套件先序序列,以二叉連結串列作為儲存結構,建立二叉樹。分別統計二叉樹中葉子結點、度為1的結點、度為2的結點的個數,並輸出。
輸入格式:
輸入擴充套件先序序列。二叉樹結點的data是字元型別資料, 其中#表示空格字元。
輸出格式:
第一行依次輸出葉子結點個數、度為1的結點個數、度為2的結點個數,以空格隔開。 第二行連續輸出葉子結點,中間不間隔。
輸入樣例:
ABC##DE#G##F###
輸出樣例:
3 2 2
CGF
//結點計算非常簡單,所有node的計算我們只需全部遍歷一遍即可,度數為1的只要判斷左右子樹中有一個存在,另一個為空即可,其他度數的結點演算法一樣
//這裡程式碼我寫的比較多主要是為了能夠將方法儘可能的都複習到,讀者可根據註釋,自行修改!
#include<stdio.h> #include<stdlib.h> #define MaxSize 100 typedef struct node{ char data; struct node *Lchild; struct node *Rchild; }BiNode, *BiTree; //擴充套件先序建立二叉樹 void Creat_Pre_Bitree(BiTree *root) { char c; c = getchar(); if(c=='#') *root = NULL; else { *root = (BiTree)malloc(sizeof(BiNode)); (*root)->data = c; Creat_Pre_Bitree(&((*root)->Lchild)); Creat_Pre_Bitree(&((*root)->Rchild)); } } int count = 0;//node int onecount = 0;//node = 1 int leafcount = 0;//node = 0 //統計二叉樹中結點數目 /* 無順序要求,任意遍歷方法均可 */ void preOrder(BiTree root) { if(root) { count++; preOrder(root->Lchild); preOrder(root->Rchild); } } //統計度數為1的結點 /* 先序(中序、後序均可)如果該節點有左(右)結點,而無右(左)結點,onecount++; */ void LfOrder(BiTree root) { if(root) { if(((root->Lchild==NULL)&&(root->Rchild!=NULL))||((root->Rchild==NULL)&&(root->Lchild!=NULL))) onecount++; LfOrder(root->Lchild); LfOrder(root->Rchild); } } //統計葉子的結點數目 /* 方法一、使用全域性變數 */ void LeafOrder(BiTree root) { if(root) { if((root->Lchild==NULL)&&(root->Rchild==NULL)) { printf("%c", root->data); } LeafOrder(root->Lchild); LeafOrder(root->Rchild); } } /* 方法二、 採用遞迴思想,如果是空樹,返回0,如果是葉子返回1,狗則,返回左右子樹的葉子結點之和。 重難點:此方法必須在左右子樹的葉子結點求出之後才可求出樹的葉子結點數。所以,我們要選擇後序遍歷 */ int leaf(BiTree root) { int nl, nr; if(root == NULL) return 0; if((root->Lchild==NULL)&&(root->Rchild==NULL)) return 1; nl = leaf(root->Lchild); nr = leaf(root->Rchild); return (nl + nr); } int main() { BiTree root; Creat_Pre_Bitree(&root); int n = leaf(root); preOrder(root); LfOrder(root); printf("%d %d %d\n", n, onecount, count - onecount - n); LeafOrder(root); return 0; }7-6 二叉樹的高度 (15 分)
從鍵盤接收擴充套件先序序列,以二叉連結串列作為儲存結構,建立二叉樹。計算二叉樹的高度,並輸出。
輸入格式:
輸入擴充套件先序序列。二叉樹結點的data是字元型別資料, 其中#表示空格字元。
輸出格式:
輸出一個整數。
輸入樣例:
ABC##DE#G##F###
輸出樣例:
5
#include<stdio.h> #include<stdlib.h> #define MaxSize 100 typedef struct node{ char data; struct node *Lchild; struct node *Rchild; }BiNode, *BiTree; int count=0; //擴充套件先序建立二叉樹 void Creat_Pre_Bitree(BiTree *root) { char c; c = getchar(); if(c=='#') *root = NULL; else { *root = (BiTree)malloc(sizeof(BiNode)); (*root)->data = c; Creat_Pre_Bitree(&((*root)->Lchild)); Creat_Pre_Bitree(&((*root)->Rchild)); } } /*求二叉樹的高度*/ /** * 方法一、使用全域性變數的方法,二叉樹的根節點為第一層的結點,第h曾結點的孩子是h+1 * */ void Treedepth(BiTree root,int h) { if(root) { if(h>count) count = h; Treedepth(root->Lchild, h + 1); Treedepth(root->Rchild, h + 1); } } /*方法二*/ /* 函式值返回:如果是空樹,則高度為0,否則它的高度應為左、右子樹高度的最大值+1,採用後序遍歷 */ int PostTreedepth(BiTree root) { int hl, hr, h; if(root==NULL) return 0; else { hl = PostTreedepth(root->Lchild); hr = PostTreedepth(root->Rchild); h = (hl > hr ? hl : hr) + 1; return h; } } int main() { BiTree root; Creat_Pre_Bitree(&root); Treedepth(root, 1); printf("%d", count); return 0; }
二叉樹的基本應用先到這些,其餘操作以後會慢慢釋出,內容有錯誤,希望大佬指正!!