完整的二叉树基本功能请看我的上一篇文章,文中介绍了二叉树的基本的几种递归操作以及每种遍历方式的三种写法
https://blog.youkuaiyun.com/CRAJA/article/details/115268295
前言:用C++的库函数写C
因为C++向C兼容,所以我们可以直接用C++运行C的代码,所以我们可以在C的代码里用C++的库函数(太爽了!!! )
不过我们还得加上这句语句
并且文件后缀要改成.cpp
using namespace std;
然后就可以开始搞事情了!
我们先看看这两个C++库函数的使用方式(C默默留下了眼泪 )
引用大佬的总结:
https://blog.youkuaiyun.com/livecoldsun/article/details/25011413
1.准备环节(头文件和结构体):
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stack>//C++栈的库
#include<queue>//C++队列的库
using namespace std;
typedef struct _BiTNode//二叉树结构体
{
Type data;//节点值
struct _BiTNode *lchild;//指向左孩子节点
struct _BiTNode *rchild;//指向右孩子节点
int flag; //用于非递归遍历 flag == 0表示没被printf(抛出) flag == 1表示被printf过过 //这个flag会被重写!!!所以只能连续非递归遍历方式一次,否则=会出错,除非重新初始化下
} BiTNode,*BiTree;
/*
作用:二叉树 根 的初始化
参数:(BiTree *)T:指向根节点的 二级指针
返回值:无
*/
void Tree_Init(BiTree *T)
{
BiTree p = (BiTree)malloc(sizeof(BiTNode));//初始化一个根节点
p->lchild = NULL;
p->rchild = NULL;
p->flag = 0;//初始化flag为0即没有被访问(printf)
T = &p;
}
我们看到这里面相比于平常的结构体里多了一个flag标记,它的作用其实就是用于非递归遍历时判断一个节点是否已经被访问(printf)过了,以防止你刚把左孩子节点给pop出栈,结果回到双亲那又给push进去造成死循环了…
2.创建二叉树并初始化标记点
/*
作用:二叉树的先序创建: #代表该节点为空
参数:(BiTree *)T:指向根节点的 二级指针
返回值:无
*/
void Tree_ProCreat(BiTree *T)
{
Type data = getchar();
if(data == '#')
*T = NULL;
else
{
(*T) = (BiTree)malloc(sizeof(BiTNode));//*T 相当于 lchild 或者 rchild (BiTNode*) 改变一级指针的指向(改变其内部存储的地址)需要使用二级指针
(*T)->data = data;
(*T)->flag = 0;//这里将二叉树的flag初始化为0
Tree_ProCreat(&(*T)->lchild);
Tree_ProCreat(&(*T)->rchild);
}
}
我们这里使用先序递归创建二叉树,不过我们加入了flag的初始化而已,其他并没有什么区别,也当然可以使用中序后序等方法创建
3.三种遍历方式
//二叉树非递归先序遍历
void Tree_ProPrint_No2(BiTree root)//先序遍历的原理就是 压入根节点打印并弹出 , 再压入左节点 , 再压入右节点 根左右
{
if(!root)
return;
stack <BiTree> Stack;//栈
Stack.push(root);//把根入栈
while(!Stack.empty())//栈不空就行
{
root = Stack.top();//获取栈顶节点
printf("%c ",root->data);//展示 核心就是这三步的位置!
Stack.pop();//抛出
root->flag = 1;//标记被抛出这其实就相当于将该节点标记为NULL不被双亲再次push进栈
if(root->rchild && root->rchild->flag==0)//先压入右孩子 这里一定要注意!先 push 右节点 再 push 左节点以至于之后每次获取到的节点都优先左节点...一直往左节点递归...
Stack.push(root->rchild);
if(root->lchild && root->lchild->flag==0)//再压入左孩子
Stack.push(root->lchild);
}
}
void Tree_MidPrint_No2(BiTree root)//根据原理 压入左节点, 再显示并弹出根节点, 再压入右节点 左根右
{
if(!root)
return;
stack <BiTree> Stack;//栈
Stack.push(root);//把根入栈
while(!Stack.empty())//栈不空就行
{
root = Stack.top();//获取栈顶节点
if(root->lchild && root->lchild->flag==0)//先遍历左孩子
Stack.push(root->lchild);
else
{
printf("%c ",root->data);//标记并弹出
Stack.pop();//抛出
root->flag = 1;//标记
if(root->rchild && root->rchild->flag==0)//再遍历右孩子
Stack.push(root->rchild);
}
}
}
//二叉树非递归后序遍历
void Tree_BackPrint_No2(BiTree root)//根据原理 先压入左节点 再压入右节点 再打印并弹出根节点 左右根
{
if(!root)
return;
stack<BiTree>Stack;
Stack.push(root);//把根入栈
while(!Stack.empty())//栈不空就行
{
root = Stack.top();//获取栈顶节点
if(root->lchild && root->lchild->flag==0)//先压入左孩子
Stack.push(root->lchild);
else if(root->rchild && root->rchild->flag==0)//再压入右孩子
Stack.push(root->rchild);
else
{
printf("%c ",root->data); //打印并弹出
root->flag = 1;//标记被抛出
Stack.pop();//抛出
}
}
}
如果你仔细观察过后会惊叹到这三个长得真TM 像,就只是访问的地方不同而已,这也就对应着先中后遍历的特性,非常好理解和记忆,如果你自己通过在纸上模拟下,你就会明白其中的奥妙
我在网上看到很多非递归方法发现先序和中序很像,但后序却和那两个与众不同,于是我想找到一种三种遍历都很相似的方法,于是得到了这个,本人也是初学者可能会有一些错误,如果您有建议,我一定洗耳恭听!