作者: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;
}
}
}
}
}
第三步:代码实现
{
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 <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;
}