二叉树遍历是一个很不错的问题,但是处理起来有点复杂,使用递归算法,会导致空间复杂度极大,而不使用递归会导致算法极难控制。因此我们使用一种线索化的算法,使得有的节点指向遍历时访问的下一个节点,从而通过很简单的算法实现遍历。
就让我们来看一下代码的实现吧:
首先是一些头文件引用和节点的构造
#include <iostream>
#include <stdlib.h>
#include <stack>
using namespace std;
enum PointTag{ LINK, THREAD }; //区别节点的指针的类别是link还是线索
template<class T>
struct BinaryTreeNode
{
T _data;
BinaryTreeNode* _left;
BinaryTreeNode* _right;
PointTag _lefttag;
PointTag _righttag;
BinaryTreeNode(const T& x)
:_data(x), _left(NULL), _right(NULL), _lefttag(LINK), _righttag(LINK)
{
}
};
然后就是这个二叉树类的实现,还有上次未完成的后续遍历的算法实现。
template<class T>
class BinaryTree
{
typedef BinaryTreeNode<T> Node;
public:
BinaryTree(const T* a,size_t n,const T& invalid )
{
size_t index = 0;
_root=create(a,n,invalid,index);
}
void inorderthread()
{
Node* prev = NULL;
_inorderthread(_root,prev);
prev->_righttag = THREAD;
}
void inorderthd()
{
_inorderthd(_root);
cout << endl;
}
void PostOrderNorm()
{
_PostOrderNorm(_root);
cout << endl;
}
protected:
Node* create(const T* a, size_t n, const T& invalid,size_t& index)
{
Node* root = NULL;
if ((a[index] != invalid) && (index < n))
{
root = new Node(a[index]);
root->_left = create(a, n, invalid, ++index);
root->_right = create(a, n, invalid, ++index);
}
return root;
}
void _inorderthd(Node* cur) //线索化后的遍历,无需使用栈,比普通使用栈的算法又简洁了不少
{
while (cur)
{
while (cur->_lefttag == LINK)
{
cur = cur->_left;
}
cout << cur->_data << " ";
while (cur&&(cur->_righttag == THREAD))
{
cur = cur->_right;
if (cur!=NULL)
cout << cur->_data << " ";
}
if (cur)
cur = cur->_right;
}
}
void _inorderthread(Node* cur,Node*& prev)//中序线索化
{
if (cur == NULL)
return;
if (cur->_left!=NULL)
_inorderthread(cur->_left, prev);
//走到这,按照顺序改访问此(cur)节点了
if (cur->_left == NULL) //此节点访问的上一个节点,为该节点的左线索
{
cur->_left = prev;
cur->_lefttag = THREAD;
}
if (prev&&(prev->_right == NULL)) //负责上个节点的右孩子的线索化
{
prev->_right = cur;
prev->_righttag = THREAD;
}
prev = cur;
if (cur->_right!=NULL)
_inorderthread(cur->_right, prev);
}
void _PostOrderNorm(Node* cur) //后续遍历的非递归算法
{
stack<Node*> s;
Node* prev = NULL; //用来标记上次访问的节点
while (!s.empty() || cur != NULL)
{
while (cur)
{
s.push(cur);
cur = cur->_left;
}
Node* top = s.top();
if (top->_right == NULL)// 叶子节点访问
{
cout << top->_data << " ";
prev = top;
s.pop();
top = s.top();
}
while (top&&(((top->_left == prev) && (top->_right == NULL)) || (top->_right == prev))) //如果该节点所有子树都遍历完毕,就该访问该节点了
{
cout << top->_data << " ";
prev = top;
s.pop();
if (s.empty())
top = NULL;
else
top = s.top();
}
if (top!=NULL) //子问题
cur = top->_right;
}
}
protected:
Node* _root;
};
测试代码如下(后序遍历和线索化要分开测试):
void testbinarytree()
{
int a[10] = { 1, 2, 3, '#', '#', 4, '#', '#', 5, 6 };
BinaryTree<int> t1(a, sizeof(a) / sizeof(a[0]), '#');
//t1.inorderthread();
//t1.inorderthd();
t1.PostOrderNorm();
}
有必要一提的是,一棵树只能有一种线索化(前序,中序,后序)。