写在前面:大家好,我是草莓橙须圆。毕业之前在CSDN和微信公众号活跃
欢迎关注我的公众号:【草莓橙须圆】
微信公众号主要就是更新大学生或者考研党的日常
CSDN主要就是学习过程中总结的笔记,以及编程分享
这篇文章主要是跟大家分享最近做的事情,关于二叉树的东西,最近看了很多网上的资料,应该没有这么全的了,还是先看运行结果吧。
输入:HA-D–MK–N-- (‘-’表示空格),A<B<C<D<…以此类推
(一)建立一颗二叉树
根据前序扩展序列创建二叉树,扩展序列指的是将二叉树中每个结点的空指针引出一个虚结点,其值为一特定值,比如“空格”。中序扩展序列和后序扩展序列不能唯一确定一个二叉树,即有不同的二叉树会对应同一个中序扩展序列或后序扩展序列。
//创建二叉树,顺序依次为中间节点->左子树->右子树
void CreatBiTree(BiTree &T)
{
char date;
date = getchar();
if (date == ' ') //如果到了叶子节点,接下来的左、右子树分别赋值为空格
T = NULL;
else {
T = new BiTreeNode;
T->date = date;
CreatBiTree(T->lchild);//递归创建左子树
CreatBiTree(T->rchild);//递归创建右子树
}
}
(二)前序、中序、后序、层次非递归遍历该二叉树
非递归实现依赖于堆栈保存遍历到的节点。
二叉树中每个节点即是父母节点的左儿子,又是下一级的根节点。非递归遍历的核心是要先遍历完左子树并压栈,再按该方法依次对各层右子树进行遍历。前序遍历和中序遍历的实现十分相似,只需改变打印结点数据指令的位置即可。因为需要考虑右节点是否存在和有没有被访问过,后序遍历的非递归实现难度更大。
前序遍历的思想:若栈不为空且q不为空时入栈即出栈并打印,然后将其右孩子入栈,再继续访问其左孩子;若q为空,则出栈。
void F_PreOrderTraverse(BiTree &T)//先序
{
BiTree q = T;
if (q == NULL)
return;
TreeList s = NULL;
while (!IsEmpty(s) || q)
{
while (q)//输出父母节点、遍历左子树
{
cout << q->date;
StackPush(s, q);
q = q->lchild;
}
if (!IsEmpty(s))
{
StackPop(s, q);
// 先压右节点再左节点是利用栈的后进先出特性,这样达到先访问左节点再访问右节点的功能
q = q->rchild;
}
}
}
用栈实现中序遍历的思想:从根节点开始,让二叉树左-中-右遍历,若不为空则入栈,若为空出栈
void F_InOrderTraverse(BiTree &T)//中序
{
BiTree q = T;
if (q == NULL)
return;
TreeList s = NULL;
while (!IsEmpty(s) || q)
{
while (q)//遍历左子树
{
StackPush(s, q);
q = q->lchild;
}
if (!IsEmpty(s))
{
StackPop(s, q);
cout << q->date;
q = q->rchild;
}
}
}
后序遍历的实现的复杂程度明显高于前序遍历和中序遍历,前序遍历和中序遍历看似实现风格一样,但是实际上前者是在指针迭代时访问结点值,后者是在栈顶访问结点值,实现思路也是有本质区别的。而这三种方法最大的缺点就是都使用嵌套循环,大大增加了理解的复杂度。
后序遍历思想为:每个节点都要进栈两次,第二次退栈是才访问节点。第一次进栈时,在遍历左子树的过程中将根节点进栈,待左子树访问完后,回溯的节点退栈,即退出这个根节点,但不能立即访问,只能借助于这个根去找该根的右子树,并遍历这棵右子树,直到该右子树全部遍历以后,再退出该根节点,并访问它。所以,为了记录节点是第一次还是第二次进栈,需单独定义一个节点作为标志。
void F_PostOrderTraverse(BiTree &T)//后序
{
if (T == NULL)
return;
BiTree Cur, Post;
Cur = T;
Post = NULL;
TreeList s = NULL; // 保存上一个节点信息
while (Cur)
{
StackPush(s, Cur);
Cur = Cur->lchild;
}
while (!IsEmpty(s))
{
StackPop(s, Cur);
if (Cur->rchild == NULL || Cur->rchild == Post)//右节点存在且没有被访问过
{
cout << Cur->date;
Post = Cur;
}
else {
//右节点已经被访问过,处理相对根节点(父母节点)
StackPush(s, Cur);
Cur = Cur->rchild;
while (Cur)
{
StackPush(s, Cur);
Cur = Cur->lchild;
}
}
}
}
层序遍历依赖于队列保存节点,且只能用非递归方法遍历。
void LevelOrder(BiTree T){
//queue<BTNode> queue;大bug隐藏在这个地方;注意queue这个容器装的是什么东西
queue<BiTree > queue;
queue.push(T); //算法:根结点入队列
while(!queue.empty()){
//若队列非空则循环执行下列的3个步骤
T = queue.front(); //步骤1:对头元素出队,指针从新指向,front()方法是将返回队头元素
cout << T->date