建立好二叉树,有时候我们需要对整个二叉树锦星遍历,即输出二叉树的所有结点元素。理论上,遍历的方式有无数种,顺序可以自己任意选定,但是绝大部分遍历方式在实际中并没有用处,比较有用的的遍历方式有两种:广度优先遍历、深度优先遍历。实现这两种遍历可以考虑利用栈以及队列的特性。
(1)广度优先遍历
广度优先遍历的意思是每次优先遍历完一层的所有子节点,遍历完N层子节点后再遍历N+1层节点,遵循的原则是自上而下、自左向右。
图1.典型二叉查找树
图中的二叉查找树使用广度优先遍历的方法,获得的结果是:8,3,10,1,6,14,4,7,13.。
要实现广度优先遍历,可以考虑使用队列实现,我们知道队列的性质是先进先出,我们先将节点8压入队列,输出节点后,弹出节点8,再将节点3和节点10压入队列(注意顺序)。接下来继续对队列处理,节点3先压入队列,所以先处理节点3,处理完之后将该节点弹出,将其左有两个节点1和6压入队列,但是要注意此时队列中还有节点10没有处理,因此先处理节点10,处理完之后弹出并将其子节点14压入队列。以此类推,二叉树的节点压入队列的顺序为
图2.广度优先遍历时节点入队顺序以及输出顺序
图中的竖线表示这些节点是在不同的循环中压入队列,按这个顺序对二叉树输出,,图中相同颜色曲线表示这些节点在相同层输出的,这样就能完成广度优先遍历,程序如下:
void Btree::BreadthFirstVisit()
{
BreadthFirstVisit(root);
}
void Btree::BreadthFirstVisit(BtreeNode *Bvs)
{
queue<BtreeNode *> nodeQueue;
nodeQueue.push(root);
while (!nodeQueue.empty())
{
BtreeNode *node = nodeQueue.front();
cout << node->ele << ' ';
nodeQueue.pop();
if (node->left)
nodeQueue.push(node->left);
if (node->right)
nodeQueue.push(node->right);
}
(1)深度优先遍历
深度优先遍历就是优先遍历完某一条线的所有子节点,遍历完之后再考虑下一条线,所遵循的顺序仍然是自上而下,自左向右。图1的二叉树,如果采用深度优先遍历原则,输出的顺序为8 3 1 6 4 7 10 14 13。
类似广度优先遍历,我们可以考虑用栈实现深度优先遍历,将节点一次压入栈,注意栈的原则是后进先出。我们首先将8压入栈,此时栈中只有8一个元素,输出并弹出,然后将其右节点10和左结点3分别压入栈,注意是先压右再压左,这样才能保证左边的先输出。接着输出节点3并弹出节点,弹出后将节点3的右节点6和左结点1分别压入栈,注意此时节点1是最后入栈的,因此需要先处理节点1,处理完节点1之后,因为节点1没有子节点了,再来处理节点6,处理完节点6后还有其左节点4和右节点7,这两个节点肯定是后入栈的,所以必然会先处理,将这些处理完之后,再回去处理节点10。以此类推。所有节点入栈的顺序为
图3. 深度优先遍历法节点入栈顺序以及输出顺序
图3显示了采用深度优先遍历是节点入栈顺序,竖线同样表示节点是在不同循环压入栈的,两种不同颜色的路线表示其中一条完成后才会进行另一条输出,红色代表节点8的左结点产生的分支,绿色部分表示节点8右节点产生的分支。深度优先遍历的程序如下
void Btree::DepthFirstVisit(BtreeNode* dvs)
{
stack<BtreeNode*> nodeStack;
nodeStack.push(root);
while (!nodeStack.empty())
{
BtreeNode *node = nodeStack.top();
cout << node->ele << ' ';
nodeStack.pop();
if (node->right)
{
nodeStack.push(node->right);
}
if (node->left)
{
nodeStack.push(node->left);
}
}
}