非递归、仅用一个栈、不加标记数组实现二叉树的后序遍历算法
2011-08-20 12:24
549 查看
作者:finallyly 出处:博客园(转载请注明作者和出处)
题记:最近在复习数据结构和算法。由于之前在数据结构和算法方面没有受过密集的训练。所以刚入门,有点吃力。最主要的一个问题是:看别人的,好像一看就全懂了,但是自己写又写不出来;给定一个具体的题目,即便是看懂了别人的算法,但是过些日子回过头来自己写,还是写不出来。和我一样出现上述状况的同学要注意啦,需要改变学习方式,否则则会一直在津津有味,乐此不疲,且自以为是地做着无用功。时间是有限的,何必浪费掉大好的时间和青春去做些无意义的事情呢?静下心来想一想,任何一本《数据结构与算法》的教材无非是想告诉我们有一些编程中可以使用的工具(栈、队列、链表、二叉树、图)以及针对如何使用这些工具应用举例而已。因此,我们只要领会到精神,就算学明白了数据结构,可是如何用数据结构实现算法呢?这体现的是自己的逻辑,永远没有什么范式,也不要去过度迷信别人。只要思路对头,你也可以设计出有效果的算法来。
对于设计算法,个人总结可以分两步:第一步:用自然语言体现出自己的思路,第二步,计算机程序亲和型的伪代码;第三步:把自己的思路用程序实现。第一步到第二步的过渡是要求,你的思路是和程序逻辑相亲和的,即用程序可以实现的。下面切入正题,讲诉非递归、仅用一个栈、不加标记数组实现二叉树的后序遍历算法的思路。
二叉树图例如上图。
第一步:思路
思路,根据二次树的结构特点,产生了如下的思路
1. 根节点入栈
2. 如果栈非空,则获取栈顶元素
3. 如果左孩子存在,则入栈左孩子
4. 如果左孩子不存在,但是右孩子存在则入栈右孩子
5. 如果左、右孩子都不存在,那么栈顶元素出栈,并访问已经出栈的栈顶元素
5.1 如果已出栈的元素是当前栈顶元素的左孩子,那么入栈右孩子
5.2如果已出栈元素是当前栈顶元素的右孩子,那么继续出栈并且访问当前栈顶元素
第二步,计算机程序亲和伪代码
思路如何变成计算机可以执行的逻辑?
考虑我们注意到 (1)5.1-5.2之间存在循环逻辑;(2)5.1 与3之间有逻辑重复。对于(1)我们可以写一个while循环,对于(2)我们可以设置一个开关变量flag,用来控制当前栈顶结点的左孩子是否参与入栈活动。
C++风格的伪代码如下
Bool PostOrderTraverse(Bitree T)
{
InitStack(S);
Push(S,T);
flag=0;
While(!Empty(S))
{
Bitree e;
GetTop(S,e);
if (e->lchild&&!flag)
{
Push(S,e->lchild);
}
else
{
if(e->rchild)
{
Push(S,e->rchild);
flag=0;
}
Else
{
Bitree pOldTop;
GetTop(S,pOldTop);
Pop(S);
Visit(pOldTop);
While(!Empty(S))
{
BiTree pNewTop;
GetTop(S,pNewTop);
if (pOldTop=pNewTop->lchild)
{
flag=1;
break;
}
else
{
Pop(S);
Visit(pNewTop);
pOldTop=pNewTop;
flag=0;
}
}
}
}
}
第三步:代码实现
bool PostOrderTraverseByStack(BiTree T)
{
stack<BiTree>bitreeStack;
bitreeStack.push(T);
int flag=0;
while(!bitreeStack.empty())
{
BiTree pTree=bitreeStack.top();
if (pTree->lchild&&!flag)
{
bitreeStack.push(pTree->lchild);
}
else//无需插入左孩子的模式
{
if (pTree->rchild)
{
bitreeStack.push(pTree->rchild);
flag=0;
}
else
{
BiTree pTopOld=bitreeStack.top();
bitreeStack.pop();
cout<<pTopOld->data<<endl;
while(!bitreeStack.empty())
{
if (!bitreeStack.empty())
{
BiTree pTopNew=bitreeStack.top();
if (pTopOld==pTopNew->lchild)
{
flag=1;
break;
}
else
{
cout<<pTopNew->data<<endl;
bitreeStack.pop();
pTopOld=pTopNew;
flag=0;
}
}
else
{
break;
}
}
}
}
}
return true;
}
为了便于大家验证,把二叉树建树、销树、主函数验证的代码张贴如下
#include <iostream>
#include <deque>
#include <stack>
using namespace std;
typedef struct BiTNode
{
char data;
BiTNode *lchild;
BiTNode *rchild;
}BiTNode,*BiTree;
bool CreateBiTree(BiTree &T,deque<char>& charQue)
{
if (!charQue.empty())
{
char ch=charQue.front();
charQue.pop_front();
if (ch==' ')
{
T=NULL;
}
else
{
T=new BiTNode;
T->data=ch;
CreateBiTree(T->lchild,charQue);
CreateBiTree(T->rchild,charQue);
}
return true;
}
return true;
}
bool PostOrderTraverse(BiTree T)
{
if (T)
{
if (PostOrderTraverse(T->lchild))
{
if (PostOrderTraverse(T->rchild))
{
cout<<T->data<<endl;
return true;
}
}
return false;
}
else
{
return true;
}
}
bool DestroyTree(BiTree &T)
{
if (T!=NULL)
{
if (DestroyTree(T->lchild))
{
if (DestroyTree(T->rchild))
{
delete T;
return true;
}
}
return false;
}
else
{
return true;
}
}
int main()
{
BiTree root;
deque<char> charQue;
charQue.push_back('A');
charQue.push_back('B');
charQue.push_back('C');
charQue.push_back(' ');
charQue.push_back(' ');
charQue.push_back('D');
charQue.push_back('E');
charQue.push_back(' ');
charQue.push_back('G');
charQue.push_back(' ');
charQue.push_back(' ');
charQue.push_back('F');
charQue.push_back(' ');
charQue.push_back(' ');
charQue.push_back(' ');
CreateBiTree(root,charQue);
PostOrderTraverse(root);
cout<<"后序遍历结束"<<endl;
PostOrderTraverseByStack(root);
cout<<"借助于栈的后序遍历结束"<<endl;
DestroyTree(root);
cout<<"删除树完毕"<<endl;
cout<<"finish"<<endl;
int f;
cin>>f;
return 0;
}
题记:最近在复习数据结构和算法。由于之前在数据结构和算法方面没有受过密集的训练。所以刚入门,有点吃力。最主要的一个问题是:看别人的,好像一看就全懂了,但是自己写又写不出来;给定一个具体的题目,即便是看懂了别人的算法,但是过些日子回过头来自己写,还是写不出来。和我一样出现上述状况的同学要注意啦,需要改变学习方式,否则则会一直在津津有味,乐此不疲,且自以为是地做着无用功。时间是有限的,何必浪费掉大好的时间和青春去做些无意义的事情呢?静下心来想一想,任何一本《数据结构与算法》的教材无非是想告诉我们有一些编程中可以使用的工具(栈、队列、链表、二叉树、图)以及针对如何使用这些工具应用举例而已。因此,我们只要领会到精神,就算学明白了数据结构,可是如何用数据结构实现算法呢?这体现的是自己的逻辑,永远没有什么范式,也不要去过度迷信别人。只要思路对头,你也可以设计出有效果的算法来。
对于设计算法,个人总结可以分两步:第一步:用自然语言体现出自己的思路,第二步,计算机程序亲和型的伪代码;第三步:把自己的思路用程序实现。第一步到第二步的过渡是要求,你的思路是和程序逻辑相亲和的,即用程序可以实现的。下面切入正题,讲诉非递归、仅用一个栈、不加标记数组实现二叉树的后序遍历算法的思路。
二叉树图例如上图。
第一步:思路
思路,根据二次树的结构特点,产生了如下的思路
1. 根节点入栈
2. 如果栈非空,则获取栈顶元素
3. 如果左孩子存在,则入栈左孩子
4. 如果左孩子不存在,但是右孩子存在则入栈右孩子
5. 如果左、右孩子都不存在,那么栈顶元素出栈,并访问已经出栈的栈顶元素
5.1 如果已出栈的元素是当前栈顶元素的左孩子,那么入栈右孩子
5.2如果已出栈元素是当前栈顶元素的右孩子,那么继续出栈并且访问当前栈顶元素
第二步,计算机程序亲和伪代码
思路如何变成计算机可以执行的逻辑?
考虑我们注意到 (1)5.1-5.2之间存在循环逻辑;(2)5.1 与3之间有逻辑重复。对于(1)我们可以写一个while循环,对于(2)我们可以设置一个开关变量flag,用来控制当前栈顶结点的左孩子是否参与入栈活动。
C++风格的伪代码如下
Bool PostOrderTraverse(Bitree T)
{
InitStack(S);
Push(S,T);
flag=0;
While(!Empty(S))
{
Bitree e;
GetTop(S,e);
if (e->lchild&&!flag)
{
Push(S,e->lchild);
}
else
{
if(e->rchild)
{
Push(S,e->rchild);
flag=0;
}
Else
{
Bitree pOldTop;
GetTop(S,pOldTop);
Pop(S);
Visit(pOldTop);
While(!Empty(S))
{
BiTree pNewTop;
GetTop(S,pNewTop);
if (pOldTop=pNewTop->lchild)
{
flag=1;
break;
}
else
{
Pop(S);
Visit(pNewTop);
pOldTop=pNewTop;
flag=0;
}
}
}
}
}
第三步:代码实现
bool PostOrderTraverseByStack(BiTree T)
{
stack<BiTree>bitreeStack;
bitreeStack.push(T);
int flag=0;
while(!bitreeStack.empty())
{
BiTree pTree=bitreeStack.top();
if (pTree->lchild&&!flag)
{
bitreeStack.push(pTree->lchild);
}
else//无需插入左孩子的模式
{
if (pTree->rchild)
{
bitreeStack.push(pTree->rchild);
flag=0;
}
else
{
BiTree pTopOld=bitreeStack.top();
bitreeStack.pop();
cout<<pTopOld->data<<endl;
while(!bitreeStack.empty())
{
if (!bitreeStack.empty())
{
BiTree pTopNew=bitreeStack.top();
if (pTopOld==pTopNew->lchild)
{
flag=1;
break;
}
else
{
cout<<pTopNew->data<<endl;
bitreeStack.pop();
pTopOld=pTopNew;
flag=0;
}
}
else
{
break;
}
}
}
}
}
return true;
}
为了便于大家验证,把二叉树建树、销树、主函数验证的代码张贴如下
#include <iostream>
#include <deque>
#include <stack>
using namespace std;
typedef struct BiTNode
{
char data;
BiTNode *lchild;
BiTNode *rchild;
}BiTNode,*BiTree;
bool CreateBiTree(BiTree &T,deque<char>& charQue)
{
if (!charQue.empty())
{
char ch=charQue.front();
charQue.pop_front();
if (ch==' ')
{
T=NULL;
}
else
{
T=new BiTNode;
T->data=ch;
CreateBiTree(T->lchild,charQue);
CreateBiTree(T->rchild,charQue);
}
return true;
}
return true;
}
bool PostOrderTraverse(BiTree T)
{
if (T)
{
if (PostOrderTraverse(T->lchild))
{
if (PostOrderTraverse(T->rchild))
{
cout<<T->data<<endl;
return true;
}
}
return false;
}
else
{
return true;
}
}
bool DestroyTree(BiTree &T)
{
if (T!=NULL)
{
if (DestroyTree(T->lchild))
{
if (DestroyTree(T->rchild))
{
delete T;
return true;
}
}
return false;
}
else
{
return true;
}
}
int main()
{
BiTree root;
deque<char> charQue;
charQue.push_back('A');
charQue.push_back('B');
charQue.push_back('C');
charQue.push_back(' ');
charQue.push_back(' ');
charQue.push_back('D');
charQue.push_back('E');
charQue.push_back(' ');
charQue.push_back('G');
charQue.push_back(' ');
charQue.push_back(' ');
charQue.push_back('F');
charQue.push_back(' ');
charQue.push_back(' ');
charQue.push_back(' ');
CreateBiTree(root,charQue);
PostOrderTraverse(root);
cout<<"后序遍历结束"<<endl;
PostOrderTraverseByStack(root);
cout<<"借助于栈的后序遍历结束"<<endl;
DestroyTree(root);
cout<<"删除树完毕"<<endl;
cout<<"finish"<<endl;
int f;
cin>>f;
return 0;
}
相关文章推荐
- 试编写一个函数,返回一颗给定二叉树在中序遍历下的最后一个节点(分别用递归和非递归实现)
- 求一个整数数组的最大元素,用递归方法实现
- 一个JavaScript递归实现反转数组字符串的实例
- 一个简单的递归实现数组组元素的组合
- 1.求第n个斐波那契数(非递归实现)。 2.一个数组中只有两个数字是出现一次,其他所有数字都出现 了两次。 找出这两个数字,编程实现。
- 通过递归判断一个整形数组是否按升序排列(java实现)
- 一个JavaScript递归实现反转数组字符串的实例
- 递归实现打印一个数组的所有排列
- 每天一个JavaScript实例-递归实现反转数组字符串
- 用java实现二叉树非递归的前序,中序,后序遍历算法
- C/C++面试题(三) 推断二叉树、快速排序递归实现、递归判断数组递增
- javascript实现 1,2,3,4,5,6,7,8,9倒序(用递归),并放入一个数组中
- 求一个整数数组的最大元素,用递归方法实现
- 每天一个JavaScript实例-递归实现反转数组字符串
- 请编程实现:产生一个int数组,长度为100,并向其中随机插入1-100,并且不能重复(百度了一下,get一种高性能算法,非递归)
- 一个数组:1,1,2,3,5,8,13,21...+m,求第30位数是多少?用递归实现;(常考!!!)
- 用递归二分法实现同时获得一个数组内的最大最小值
- 请编程实现:产生一个int数组,长度为100,并向其中随机插入1-100,并且不能重复(百度了一下,get一种高性能算法,非递归)
- 怎样编写一个程序,把一个有序整数数组放到二叉树中? 编写实现链表排序的一种算法。说明为什么你会选择用这样的方法?
- 每日一个算法------二叉树实现、递归和非递归算法(c++版)