- 10.4-1 那棵树就长成下面这个样子
/*
18
12 10
7 4 2 21
5
*/
下面就借用10.4-1提供的数据,构建一棵树,然后分别对它做10.4-2所要求的递归遍历和10.4-3所要求的非递归遍历。
递归遍历的方式有三种,前序、中序、后序,实现上的差异,无非是把Traverse1(Index)函数里后三句的顺序换一换。
非递归遍历,简单的算法是借助一个栈或者队列,同样有前序、中序、后序形式。复杂一些的算法就是下面的10.4-5。
10.4-5要求实现一种非递归的遍历方法,且除了树本身外,只使用固定存储空间。
反思一下为什么Traverse2需要使用O(n)的额外存储空间呢?那是因为一个Node出栈的同时,最多情况下,会向栈中压入左右孩子两个新节点,所以栈的空间是呈线性增长的。如果要限制存储空间为常量,就必须放弃“通过一个旧节点找到两个新节点”的做法,取而代之以“用一个旧节点找到一个新节点”的做法。
于是就有了下面的问题,一方面,每个节点的职责是保证自己的两个孩子都被遍历到,另一方面,受到存储的限制,通过当前节点只可以找出一个新节点,,怎么办呢?只好先找左孩子,再通过左孩子找自己,然后再通过自己找右孩子(左右顺序无所谓,先右再左也一样)。所以,整个过程中当前节点指针的行走方向一共有三种状态,1.从父亲找到的自己;2. 从左孩子找到的自己 3. 从右孩子找到的自己。每种状态下当前节点该做的工作是不一样的。具体实现见Traverse3。
#include <iostream>
#include <stack>
using namespace std;
class BinaryTree
{
public:
using Index = int;
BinaryTree();
//O(n)-time recursive traverse 10.4-2
void Traverse1() const;
void Traverse1(Index root) const;
//O(n)-time nonrecursive traverse10.4-3
void Traverse2() const;
//O(n)-time nonrecursive traverse,with constant extra space 10.4-5
void Traverse3() const;
private:
struct Node
{
int key;
Index parent;
Index left;
Index right;
};
Node m_array[11];
Index m_root;
};
BinaryTree::BinaryTree()
{
m_root = 6;
m_array[1] = Node{12,6,7,3};
m_array[3] = Node{4,1,10,-1};
m_array[4] = Node{10,6,5,9};
m_array[5] = Node{2,4,-1,-1};
m_array[6] = Node{18,-1,1,4};
m_array[7] = Node{7,1,-1,-1};
m_array[9] = Node{21,4,-1,-1};
m_array[10] = Node{5,3,-1,-1};
}
void BinaryTree::Traverse1() const
{
Traverse1(m_root);
}
void BinaryTree::Traverse1(Index root) const
{
if(root == -1)
return;
cout << m_array[root].key << "\t";
Traverse1(m_array[root].left);
Traverse1(m_array[root].right);
}
void BinaryTree::Traverse2() const
{
stack<Index> indexStack;
indexStack.push(m_root);
int index,childIndex;
while(!indexStack.empty())
{
index = indexStack.top();
indexStack.pop();
cout << m_array[index].key << "\t";
childIndex = m_array[index].right;
if(childIndex != -1)
indexStack.push(childIndex);
childIndex = m_array[index].left;
if(childIndex != -1)
indexStack.push(childIndex);
}
}
void BinaryTree::Traverse3() const
{
enum
{
Parent,
Left,
Right
};
int come_from = Parent;
Index index_next = m_root,index;
bool back_to_parent;
while(true)
{
index = index_next;
back_to_parent = false;
if(come_from == Parent) //从父亲找到自己,我的任务是打印自身,然后优先找可能存在的左孩子
{
cout << m_array[index].key << "\t";
index_next = m_array[index].left;
if(index_next == -1)
index_next = m_array[index].right;
if(index_next == -1)
back_to_parent = true;
}
else if(come_from == Left) //从左孩子返回到自己,我的任务是找可能存在的右孩子
{
index_next = m_array[index].right;
come_from = Parent;
if(index_next == -1)
back_to_parent = true;
}
else //从右孩子返回到自己,我的任务是返回到我的父亲
{
back_to_parent = true;
}
if(back_to_parent)
{
index_next = m_array[index].parent;
if(index_next == -1)
break;
if(m_array[index_next].left == index)
come_from = Left;
else
come_from = Right;
}
}
}
void Test()
{
BinaryTree tree;
tree.Traverse1(); //18 12 7 4 5 10 2 21
cout << endl;
tree.Traverse2(); //18 12 7 4 5 10 2 21
cout << endl;
tree.Traverse3();
}
/* 调用Test,运行结果为
18 12 7 4 5 10 2 21
18 12 7 4 5 10 2 21
18 12 7 4 5 10 2 21
*/