在说链式二叉树的遍历之前,我们先来说一下如何创建一个链式二叉树。创建一个链式二叉树,需要一个已知的字符串,这个字符串必须支持创建二叉树的规则。比如:
我们通过递归来实现二叉树的创建,开始先向左走,如果遇到#,就返回,然后向右走,直到遇到#,就返回一个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);
}