机会是留个有准 备的人的,如果你没有得到机会,那只能说明你准备不足。
又一次的机会失掉了,好像已经错过了太多的机会,后悔也是没用的,好好总结,继续往前走吧。
这个二叉树,总是感觉并不是难的,尤其是前中后序的遍历,可是人们总是想当然的以为,真正的自己写时,才发现,原来一切都高估自己了,尤其是用笔写在纸上,如果你能一次写正确,而且不需要涂改,那么,你真的很牛了(个人比较菜)。二叉树,多么经典的数据结构,为什么自己没有早点认认真真的写一遍呢!好了,不罗嗦了,还是进入正题吧。 二叉树的性质,我就不提了,应该都知道。我在这里主要是写了一下二叉树的遍历,包括递归算法和非递归算法。递归算法非常的简单,几行的代码。其实,将递归的算法转变成非递归的算法,其实就是用一个数据结构来压栈,递归,本来就是一个压栈的过程,所以说栈这种数据结构是有记忆性的。所以在写非递归算法时,我们需要额外的空间来压栈。在这里我直接使用了STL中的stack,其实也可以自己建立一个数组,通过压栈过程中调整top指针的位置也能实现栈的功能。 我先顶一个两个struct,一个是Tree,一个是node其中Tree就是二叉树结构。node包含一个二叉树节点和一个标记值,此值代表的意思是此节点的右孩子是否访问过,若访问过则为true,否则为false。定义如下:
struct Tree
{
int data;
Tree *left;
Tree *right;
};
struct node
{
Tree *root;
bool mask;
};
接着,我定义了一个创建二叉树的函数,也是用递归来实现的。代码如下:
Tree *CreateBinaryTree()
{
Tree *root;
int n;
cin >> n;
if(0 == n)
{
return NULL;
}
else
{
root = new Tree;
root->data = n;
cout << "输入节点" << root->data << "的左孩子:";
root->left = CreateBinaryTree();
cout << "输入节点" << root->data << "的右孩子:";
root->right = CreateBinaryTree();
return root;
}
}
此函数,在输入时,当输入为0时,表示孩子为空,我测试时的输入序列为:1 2 4 8 0 0 9 0 0 5 10 0 0 0 3 6 0 0 7 0 11 0 0,呵呵,挺麻烦的。
下面是递归算法,实现,前序 中序 后序的遍历。代码如下:
// 递归
void PreOrderTravel(Tree *root)
{
if(root == NULL)
return;
printf("%d ",root->data);
PreOrderTravel(root->left);
PreOrderTravel(root->right);
}
void InOrderTravel(Tree *root)
{
if(root == NULL)
return;
InOrderTravel(root->left);
printf("%d ",root->data);
InOrderTravel(root->right);
}
void BackOrderTravel(Tree *root)
{
if(root == NULL)
return;
BackOrderTravel(root->left);
BackOrderTravel(root->right);
printf("%d ",root->data);
}
递归是算法简单吧,就是短短的几行,而且前序 中序 后序,只是调整了一下输出时的位置。
下面是非递归算法,对于前序和中序的非递归算法,对于我来说不算太难,主要是写的认真,而且要考虑清楚整个流程,这样应该会很快写出来。我在写后序遍历的非递归算法时,着实花费了好长时间,主要是我不清楚这个标记怎么来做。在这里,记录一下:根据后序遍历的特点,根节点肯定是最后输出,而且根节点输出位置在右孩子后面,所以,我们标记时,可以这样,如果当前节点有右孩子,而且没有被访问过,那么当前节点肯定不能输出,只有当当前节点没有右孩子,或者右孩子已经访问过了以后才可以输出当前节点,主要思想就是这样,标记的右孩子是否被访问过。代码如下:
// 非递归
void PreOrderTravel(Tree *root,stack<Tree *> st)
{
// 依次沿左孩子压栈
while(root)
{
printf("%d ",root->data); // 输出头结点
st.push(root);
root = root->left;
}
while(st.size() != 0)
{
root = st.top();
st.pop();
if(root->right) // 判断栈顶元素是否有右孩子
{ // 如果右孩子存在,则沿左孩子依次压栈
root = root->right;
while(root)
{
printf("%d ",root->data);
st.push(root);
root = root->left;
}
}
}
}
void InOrderTravel(Tree *root,stack<Tree*> st)
{
while(root)
{
st.push(root);
root = root->left;
}
while(st.size() != 0)
{
root = st.top();
printf("%d ",root->data);
st.pop();
if(root->right)
{
root = root->right;
while(root)
{
st.push(root);
root = root->left;
}
}
}
}
void BackOrderTravel(Tree *root,stack<node *> st)// 后序遍历的非递归算法,需要一个标记,来指示右孩子是否被访问过
{ // 如果右孩子存在,且访问过,或者,右孩子不存在,那么输出当前节点
while(root) // 如果右孩子存在,但没有访问过,那么标记当前为1,依次沿左孩子压栈
{
node *tmp = new node;
tmp->root = root;
tmp->mask = 0;
st.push(tmp);
root = root->left;
}
while(st.size()!= 0)
{
node *t2 = st.top();
root = t2->root;
if((root->right && t2->mask) || (root->right == NULL) )
{
printf("%d ",root->data);
st.pop();
}
else
{
t2->mask = 1;
root = root->right;
while(root)
{
node *t3 = new node;
t3->root = root;
t3->mask = 0;
st.push(t3);
root = root->left;
}
}
}
}
上面就是完整的代码,写一遍,好让自己记忆吧,如果来访者发现错误,可以留言啊,大家共同学习吧,一起向着大牛前进。。。