在阐述我的思路之前,我们先来看看递归遍历二叉树到底发生了什么,以中序遍历为例:
void InOrder(BinaryTreeNode<T>* node)
{
if(!node)return;//第一行
InOrder(node->leftChild);//第二行
cout << node->data << " ";//第三行
InOrder(node->rightChild);//第四行
}
1:我们将节点传入这个函数后,执行第二行代码的时候程序先将当前的node压栈,然后将node的左子树传入一个新的InOrder函数
2:在新的左子树中,如果node为空,则将刚才压栈的node弹出并输出其data,并将node的左子树传入一个新的InOrder函数,执行1,否则,执行1。
简单总结一下,循环遍历函数需要做的只有以下几件事:将节点压栈,将节点弹栈,判断节点是否为空,输出节点的data。当然,还有一个最重要的环节:控制函数在什么时候执行上述四件事。
对函数的控制环节,我们采用标记的方法来记录函数当前需要做什么,下一步需要做什么。具体标记值如下:
00 什么也不做,即该节点已经访问完毕
01 输出该节点的值
10 将节点的左子树压栈
11 将节点的右子树压栈
所以,我们可以用一个统一的函数来描述这个过程:
template<typename T>
void BinarySearchTree<T>::Order(const char _flag)
{
stack.Push(root, _flag);//0001 1110
BinaryTreeNode<T>* p;
char flag;
while (!stack.IsEmpty())
{
stack.Pop(p, flag);
if (!p)continue;
switch (flag & 0x03)
{
case 0:
break;
case 1:
cout << p->data << " ";
stack.Push(p, flag >> 2);
break;
case 2:
stack.Push(p, flag >> 2);
stack.Push(p->leftChild, _flag);
break;
case 3:
stack.Push(p, flag >> 2);
stack.Push(p->rightChild, _flag);
break;
}
}
cout << endl;
}
栈的结构我会在稍后做介绍。利用这个函数,我们只需要传入一个_flag就可以决定对二叉树进行何种方式进行遍历,例如,中序遍历为0x36(00110110),先序遍历为0x39(00111001),后序遍历为0x1e(00011110)。只有在节点弹栈后flag的最后两位为00时,我们才确定该节点已经彻底处理完毕,不再对其进行处理,否则我们都将该节点进行压栈,并修改其对应的flag值。当然,这个函数存在一定的效率问题,可以利用这个原理将函数进行优化,分别写出对应的先序、中序、后序遍历函数。
现在我们看一下栈的相关结构。由于遍历函数需要频繁的进行弹栈与压栈,并且二叉树的节点数量我们不确定,所以我采用了链表的思路并在链表的基础上做了一点修改。每一个节点可以存储十个data,当节点存满时,新建一个节点;当节点为空时,删除该节点。栈与栈节点的代码如下:
template <typename TreeNode>
class OrderListNode
{
friend class OrderList<TreeNode>;
public:
OrderListNode() { currentData = 0; }
protected:
OrderListNode<TreeNode>* link;
TreeNode data[10];
int currentData;
char flag[10];
};
template <typename TreeNode>
class OrderList
{
public:
OrderList();
~OrderList();
virtual OrderList<TreeNode>& Push(__in TreeNode _data, __in char _flag);
virtual OrderList<TreeNode>& Pop(__out TreeNode &_data, __out char& _flag);
virtual void Output();
virtual bool IsEmpty() { return top == nullptr; }
protected:
OrderListNode<TreeNode>* top;
};
对应的.cpp为:
template <typename TreeNode>
OrderList<TreeNode>::OrderList()
{
top = nullptr;
}
template<typename TreeNode>
OrderList<TreeNode>::~OrderList()
{
OrderListNode<TreeNode>* p = top;
while (p)
{
p = top->link;
delete top;
top = p;
}
}
template<typename TreeNode>
OrderList<TreeNode>& OrderList<TreeNode>::Push(__in TreeNode _data, __in char _flag)
{
if (!top)
{
top = new OrderListNode<TreeNode>;
top->link = nullptr;
}
if (top->currentData >= 9)
{
OrderListNode<TreeNode>* p = new OrderListNode<TreeNode>;
p->link = top;
top = p;
}
top->data[top->currentData] = _data;
top->flag[top->currentData] = _flag;
top->currentData++;
return *this;
}
template<typename TreeNode>
OrderList<TreeNode>& OrderList<TreeNode>::Pop(__out TreeNode &_data, __out char& _flag)
{
if (!top)
return *this;
_data = top->data[--top->currentData];
_flag = top->flag[top->currentData];
if (top->currentData == 0)
{
OrderListNode<TreeNode>* p = top->link;
delete top;
top = p;
}
return *this;
}
template<typename TreeNode>
void OrderList<TreeNode>::Output()
{
OrderListNode<TreeNode>*p = top;
while (p)
{
for (int i = 0; i < p->currentData; ++i)
cout << p->data[i] << " ";
p = p->link;
}
cout << endl;
}
在文章的最后,我想与诸位一起探讨一种新的二叉树删除的方法:采用二级指针对节点进行标记,用这种方法可以减少一些分支语句,即不需要判断删除的是否为根节点,以及要删除的是某一节点的左子树还是右子树,代码由鄙人独立设计,引用或复制还望诸位加以备注,如有不足或更好的方法,也望多多指教。代码如下:
template <typename T>
BinarySearchTree<T>& BinarySearchTree<T>::Delete(__in const T _data)
{
if (!root)
return *this;
BinaryTreeNode<T> **pCurrent = &root;
while ((*pCurrent) && (_data != (*pCurrent)->data))
{
if (_data < (*pCurrent)->data)
pCurrent = &((*pCurrent)->leftChild);
else
pCurrent = &((*pCurrent)->rightChild);
}
if (!(*pCurrent))
{
cout << "Find Nothing\n";
return *this;
}
BinaryTreeNode<T>* remove = (*pCurrent);
if (!remove->leftChild)//leftchild is nullptr
{
if (!remove->rightChild)//remove has no child
{
delete remove;
(*pCurrent) = nullptr;
return *this;
}
(*pCurrent) = remove->rightChild;
delete remove;
return *this;
}
if (!remove->rightChild)//only rightchild is null
{
(*pCurrent) = remove->leftChild;
delete remove;
return *this;
}
//remove has two children
BinaryTreeNode<T> **temp = &(remove->leftChild);
while (!(*temp)->rightChild)
{
temp = &(*temp)->rightChild;
}
(*pCurrent) = (*temp);
(*temp)->leftChild = remove->leftChild;
(*temp)->rightChild = remove->rightChild;
temp = nullptr;
delete remove;
return *this;
}