在說鏈式二叉樹的遍歷之前,我們先來說一下如何創建一個鏈式二叉樹。創建一個鏈式二叉樹,需要一個已知的字符串,這個字符串必須支持創建二叉樹的規則。比如:
我們通過遞歸來實現二叉樹的創建,開始先向左走,如果遇到#,就返回,然後向右走,直到遇到#,就返回一個NULL,這樣,就完成了一個鏈式二叉樹的創建。
這裏先將除了主要函數外的其他調用到的函數放在這裏,供查閱
頭文件
#ifndef _BTREE_H__
#define _BTREE_H__
#include<stdio.h>
#include<stdlib.h>
typedef char BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* lchild;
struct BinaryTreeNode* rchild;
}BTNode;
typedef BTNode* QUDataType;
typedef BTNode* STDataType;
typedef struct Stack
{
STDataType* _a;
int _top;
int _capacity;
}Stack;
typedef struct QueueNode
{
struct QueueNode* _next;
QUDataType _data;
}QueueNode;
typedef struct Queue
{
QueueNode *head;
QueueNode *rear;
}Queue;
BTNode* BinaryTreeCreate(BTDataType* src);
void BinaryTreePrevOrder(BTNode* root);
void BinaryTreeInOrder(BTNode* root);
void BinaryTreePostOrder(BTNode* root);
void BinaryTreeLevelOrder(BTNode* root);
void BinaryTreePrevOrderNonR(BTNode* root);
void BinaryTreeInOrderNonR(BTNode* root);
void BinaryTreePostOrderNonR(BTNode* root);
void BIaryTreeLevelPrint(BTNode* root, int num);
#endif//_BTREE_H__
BTNode* BinaryTreeCreate(BTDataType* src)//鏈式二叉樹的創建
{
static int n = 0;
if (src[n] == '#')
{
n++;
return NULL;
}
BTNode *cur = (BTNode*)malloc(sizeof(BTDataType));
cur->data = src[n];
n++;
cur->lchild = BinaryTreeCreate(src);
cur->rchild = BinaryTreeCreate(src);
return cur;
}
void QueueInit(Queue* qu)//隊列的初始化
{
qu->head = qu->rear = (QueueNode *)malloc(sizeof(QueueNode));
}
void QueuePop(Queue* qu)//出對頭
{
qu->head = qu->head->_next;
}
void QueueDestory(Queue* qu)//銷燬隊列
{
free(qu->head);
}
void QueuePush(Queue* qu, QUDataType x)//隊列中進入元素
{
qu->rear->_data = x;
qu->rear->_next = (QueueNode *)malloc(sizeof(QueueNode));
qu->rear = qu->rear->_next;
qu->rear->_data = NULL;
}
STDataType StackTop(Stack * st)//拿取棧頂
{
return st->_a[st->_top - 1];
}
void StackInit(Stack *st)//棧的初始化
{
st->_a = (STDataType)malloc(sizeof(STDataType)* 10);
st->_capacity = 10;
st->_top = 0;
}
void StackPop(Stack *st)//退棧
{
st->_top--;
}
void StackPush(Stack *st,STDataType x)//壓棧
{
if (st->_top == st->_capacity - 1)
{
realloc(st->_a, sizeof(STDataType)* 2 * st->_capacity);
}
st->_a[st->_top] = x;
st->_top++;
}
void StackDestory(Stack *st)//銷燬棧
{
free(st->_a);
}
二叉樹的鏈式結構的7種遍歷方法
遞歸遍歷:
- 前序遍歷
前序遍歷的思路是,通過遞歸的方式,讓鏈式二叉樹,先不停地從左孩子向下,每遇到一個節點,就進行該節點打印,直到遇到“#”,然後又從右孩子開始向下走,直到遇到“#”,就返回上一個節點,然後又從這個節點開使向右孩子的位置向下。
void BinaryTreePrevOrder(BTNode* root)//遞歸式的前序遍歷
{
printf("%c ", root->data);
if (root->lchild)
{
BinaryTreePrevOrder(root->lchild);
}
if (root->rchild)
{
BinaryTreePrevOrder(root->rchild);
}
}
- 中序遍歷
中序遍歷和前序遍歷一樣,唯一的差別是,先進型向左孩子的查找,然後進行一個,遇到“#”後,就進行該節點的打印,然後再進行右孩子的查找。
void BinaryTreeInOrder(BTNode* root)//遞歸式的中序遍歷
{
if (root->lchild)
{
BinaryTreeInOrder(root->lchild);
}
printf("%c ", root->data);
if (root->rchild)
{
BinaryTreeInOrder(root->rchild);
}
}
- 後續遍歷
和中序,前序一樣,也僅僅只是打印的順序發生了改變。
void BinaryTreePostOrder(BTNode* root)//遞歸式的後序遍歷
{
if (root->lchild)
{
BinaryTreePostOrder(root->lchild);
}
if (root->rchild)
{
BinaryTreePostOrder(root->rchild);
}
printf("%c ", root->data);
}
層序遍歷:
層序遍歷是通過一個隊列來實現的,通過利用隊列先入後出的原則,來完成層序遍歷。思路如下:從根節點開始,先將根節點打印,然後如果有左孩子,就將左孩子入隊列,如果有右孩子,就將右孩子入隊列。然後從隊列中出一個元素,將這個元素打印,然後如果有左孩子,就將左孩子入隊列,如果有右孩子,就將右孩子入隊列,就這樣,如果有一個孩子是NULL;那就看另外一個孩子,如果都是NULL,那就只進行一個出隊列的打印就好了,這樣,層層循環,就完成了層序遍歷。
void BinaryTreeLevelOrder(BTNode* root)//層序遍歷
{
Queue qu;
QueueInit(&qu);
while (root)
{
putchar(root->data);
if (root->lchild)
{
QueuePush(&qu, root->lchild);//入隊
}
if (root->rchild)
{
QueuePush(&qu,root->rchild);//入隊
}
root = qu.head->_data;//拿取對頭
QueuePop(&qu);//出對頭
}
//QueueDestory(&qu);
}
非遞歸的遍歷:
- 前序遍歷
用非遞歸方式的前序遍歷,是利用自己模擬一個棧,通過棧的先入後出的原則,進行一個前序遍歷。思路如下:我們在一開始,先將根節點打印,然後將根節點的右孩子進棧(右孩子不爲NULL),將根節點變爲左孩子(左孩子不爲NULL),然後繼續執行的讓根節點的右孩子進棧,打印根節點,直到遇到NULL,這個時候,我們已經將左孩子遍歷了一遍,需要進入右孩子了,我們進行一個拿取棧頂的操作,然後讓節點變爲棧頂的那個元素,同時進行一次退棧,這個時候,拿到地棧頂就是這個葉子節點的根節點的右孩子了,這樣進行一個循環,就完成了非遞歸的前序遍歷。
void BinaryTreePrevOrderNonR(BTNode* root)//非遞歸式的前序遍歷
{
Stack st;
StackInit(&st);
while (root)
{
putchar(root->data);//先進行一個根節點的打印
if (root->rchild)
{
StackPush(&st,root->rchild);//右孩子進棧
}
if (root->lchild)
{
root = root->lchild;//左孩子進棧
}
else
{
if (st._top == 0)//判斷在退棧之前 棧是否是空
{
break;
}
root = st._a[st._top - 1];
StackPop(&st);
}
}
}
- 中序遍歷
思路:中序遍歷,是先進行一個根節和根節點所有左孩子的進棧,然後在遇到根節點的左孩子是NULL時,然後根節點變爲棧頂的元素,進行一個根節點的打印,進行一次退棧操作,然後去根節點的右孩子的位置,繼續進行一個根節點的左孩子進棧操作。就這樣,利用一個循環,完成這樣的非遞歸式的中序遍歷。
void BinaryTreeInOrderNonR(BTNode* root)//非遞歸式的中序遍歷
{
BTNode *cur = root;
Stack st;
StackInit(&st);
while (1)
{
for (;cur;cur = cur->lchild)//所有根節點的左孩子進棧
{
StackPush(&st,cur);
}
cur = StackTop(&st);//拿取棧頂
putchar(cur->data);
StackPop(&st);//退棧
cur = cur->rchild;//進入右孩子
if (!cur)
{
if (st._top == 0)//判斷在退棧之前 棧是否是空
{
break;
}
}
}
StackDestory(&st);
}
- 後序遍歷
後續遍歷的要求是,在左孩子和右孩子都已經進行了打印後,纔打印父親節點,這樣的話,我們就要直到從左孩子回來時,父親節點被訪問的次數,因爲父親節點聯繫着左右兩個孩子節點,二叉樹通過左節點回來的根節點,需要經過一個自己的父親節點,然後才能去到右孩子的位置,因此,我們需要一個標記,來標出,父親節點被訪問的次數。如果父親節點沒有被左孩子返回時訪問,這個標記就是0,如果被左孩子返回訪問了一次,就記做1。這時,我們可以定義一個數組,這個數組的下表等於棧空間中父親節點的位置。我們通過進行最初始的根節點的入棧開始,一直向左孩子方向進行入棧操作,直到遇到NULL停止,這個時候,棧頂的下標就代表了自己在數組中的訪問次數,我們進行一個拿取棧頂的操作,同時將數組中這個節點的位置,進行一個置1的操作,這樣,如果下一次拿去棧頂的時候,如果發現這個棧頂在數組中的數是1的時候,就代表這個棧頂,已經被訪問過一次,就進行一個棧頂的打印,這裏的打印是一個循環打印,如果根節點及他的父親節點的標記都是1的話,就依次向上進行打印,直到遇到0的位置停止,每次打印完了就進行一次退棧。最後通過一個循環,就完成了一個非遞歸式的後序遍歷。
void BinaryTreePostOrderNonR(BTNode* root)//非遞歸式的後序遍歷
{
BTNode *cur = root;
Stack st;
int arr[100] = { 0 };//創建一個數組
StackInit(&st);
while (1)
{
for (;cur;cur = cur->lchild)//所有根節點的左孩子進棧
{
StackPush(&st, cur);
arr[st._top - 1] = 0;
}
while (arr[st._top - 1])//關鍵所在,判斷父親節點是否是的數組元素是否是1,若是,則進行一個循環打印,直到遇到是0的停止
{
if (st._top == 0)//判斷在退棧之前 棧是否是空
{
break;
}
cur = StackTop(&st);//拿取棧頂
putchar(cur->data);
StackPop(&st);//退棧
}
if (st._top == 0)//判斷在退棧之前 棧是否是空
{
break;
}
cur = StackTop(&st);//拿取棧頂
arr[st._top - 1] = 1;//將該父親節點的數組元素置1
cur = cur->rchild;//進入右孩子
}
StackDestory(&st);
}