前言
以下是本博主用c语言创建栈和队列的博文
在二叉树遍历的时候,默认是先左子树后右子树。先,中,后是针对什么时候读取根节点而言的。
先序与中序的非递归过程可以通过这个图片来理解。先序,中序与后序都需要借助栈来实现。对于c没有栈,所以本博主在代码中的栈是自己用c语言写的栈(链式栈),你们也可以依照自己的来改。在c++中就可以直接用STL中的了。
树的存储结构
//树的节点
typedef struct BiTNode {
char data;
struct BiTNode* left, * right;
}BiTNode,*BiTree;
先序遍历
先序的递归遍历
/* 先序遍历*/
//递归算法
void PreOrder(BiTree T) {
if (T) {
printf("%c ", T->data);
PreOrder(T->left);
PreOrder(T->right);
}
}
先序的非递归遍历
算法思路
- 先将根节点压入栈中,用一个工作指针node(初始值为根节点)
- 步骤1 遍历当前节点node的左子树,直到左子树为空。(在这里输出)
- 步骤2 弹出栈顶的空指针
- 步骤3 如果栈没有空,将栈顶元素弹出赋值给node,将node->right压入栈
代码:
void PreOrder_S(BiTree T) {
if (!T) return;
LinkStack S = (SNode*)malloc(sizeof(SNode));
init(S);//初始化链式栈,使其置为空
Push_L(S, T); //将根节点压入
BiTNode* node = Top_L(S);
while (!empty(S)) {
// 一直往左子树方向走,直到遇到空的节点
while (node) {
printf("%c ", node->data);
node = node->left; //置当前节点为其左子树
Push_L(S, node); //压入
}
//上面循环结束的时候,在最后压入了一个空节点
Pop_L(S); //弹出空节点
if (!empty(S)) {
//从栈顶弹出元素赋值给node
node = Top_L(S);
Pop_L(S);
node = node->right;//置为其右子树
Push_L(S, node);//压入
}
}
}
中序遍历
中序的递归遍历
/* 中序遍历*/
//递归算法
void InOrder(BiTree T) {
if (T) {
InOrder(T->left);
printf("%c ", T->data);
InOrder(T->right);
}
}
中序遍历的非递归算法
算法的基本思路:
- 先将根节点压入栈中,用一个工作指针node(初始值为根节点)
- 步骤1 遍历当前节点node的左子树,直到左子树为空。
- 步骤2 弹出栈顶的空指针
- 步骤3 如果栈没有空,将栈顶元素弹出赋值给node(在这里输出),将node->right压入栈
代码:
void InOrder_S(BiTree T) {
if (!T) return;
LinkStack S = (SNode*)malloc(sizeof(SNode));
init(S);
Push_L(S, T);
BiTNode* node = Top_L(S);
while (!empty(S)) {
while (node) {
node = node->left;
Push_L(S, node);
}
Pop_L(S);//空指针退栈
if (!empty(S)) {
//从栈顶弹出元素赋值给node
node = Top_L(S);
Pop_L(S);
printf("%c ", node->data);
node = node->right;//置为其右子树
Push_L(S, node);//压入
}
}
}
后序遍历
后序的递归遍历
/* 后序遍历*/
//递归算法
void PostOrder(BiTree T) {
if (T) {
PostOrder(T->left);
PostOrder(T->right);
printf("%c ", T->data);
}
}
后序的非递归遍历
算法的基本思路:
- 因为后序遍历的时候一个二叉树的根节点会访问两次
- 设置一个标记,在第一次访问的时候不用出栈,只用读取即可
- 在第二次访问时就出栈
- 步骤一 遍历当前节点node的左子树,直到左子树为空。
- 步骤二 弹出栈顶的空指针,设置flag = 1,T = NULL
- 步骤三 遍历该节点的右子树,如果右子树为空,或者已经访问两次了就出栈
- 否则 就node = node->right,然后将node压入栈中
代码:
void PostOrder_S(BiTree T) {
if (!T) return;
LinkStack S = (SNode*)malloc(sizeof(SNode));
int flag;
init(S);
Push_L(S, T);
BiTNode* node = Top_L(S);
while (!empty(S)) {
//向左移动到尽头
while (node) {
node = node->left;
Push_L(S, node);
}
//空指针退栈
Pop_L(S);
//T指向 栈顶节点前一个已经访问过的节点
//初始值为NULL,是栈顶节点右子树为空也可以直接输出了
T = NULL;
flag = 1;
while (!empty(S) && flag) { //变循环了
//取出当前栈顶元素
node = Top_L(S);
//右节点为空,或者右节点已经访问过了
if (node->right == T) {
printf("%c ", node->data);
Pop_L(S);
//T指向被访问的节点
T = node;
}
else {
//否则将右节点插入
node = node->right;
Push_L(S, node);
//设置未被访问的标记
flag = 0;
}
}
}
}
层次遍历
/*层次遍历*/
void LevelOrder(BiTree T) {
if (!T)return;
LinkQueue Q;
init(Q); //初始化队列
push(Q, T);//将根节点压入队列
while (!empty(Q)) {
BiTNode* node = font(Q);
pop(Q);
printf("%c ", node->data);
if (node->left)
push(Q,node->left);
if (node->right)
push(Q, node->right);
}
}
层次遍历获得每一层的节点个数
思路:想比于普通的层次遍历只循环一次且只用到一个队列,这个算法需要用到两个队列,并且在一个循环中,还要嵌套一个循环。之前在每一次都将不为空的节点插入一个队列中,因此除非节点全部遍历在停止而不能得到具体的层的节点。因此我们将左右孩子的节点先放在一个辅助队列里边。直到将原队列的节点遍历完,那么该次遍历的就是该层节点,辅助队列里边的则是下一层的节点,在内层循环结束后,将辅助队列的元素赋值给原队列,同时层数+1。
void traverseByLevel(node* root) {
if (root) {
queue<node*>que;
que.push(root);
int level = 1; //记录当前层数
while (que.size()) {
queue<node*>assist;
if (que.front() != root) { //第一行后都需要输出换行
cout << endl;
}
bool flag = true;
cout << "第" << level << "有" << que.size() << "个结点" << endl;
while (que.size()) {
node* p = que.front();
que.pop();
if (p->lchild) {
assist.push(p->lchild);
}
if (p->rchild) {
assist.push(p->rchild);
}
if (false == flag) { //每行第2个元素开始 输出元素时前面加空格
cout << " ";
}
cout << p->data ;
flag = false;
}
level++;
que = assist;
}
}
}