层次遍历二叉树是面试中经常被要求手写的算法,下面来分析遍历过程。
1.先来写一个算法,打印二叉树中某层次的节点(从左到右),其中根节点为第0层。
函数原型:int PrintNodeAtLevel(Node *pRoot,int level),成功返回1,失败返回0。
10
/ \
5 12
/ \
4 7
打印第1层节点输出:5,12。
思路分析:
肯定要使用先序遍历,考虑使用递归方式,每当递归深入一层level减1,当level=0时输出相应节点。但是这个方法效率比较低,后面我们会改进。
多次强调,递归的退出条件是需要仔细考虑的地方。如果pRoot=NULL或者level<0 这个时候要返回0,如果level=0,要输出节点并返回1。如果上面两点都没有返回,那么就进入递归。
int PrintNodeAtLevel(Node *pRoot,int level)
{
//返回0不在往更深层递归
if(pRoot==NULL || level<0)
{
return 0;
}
//满足条件,输出返回,不在往深层递归
if(level==0)
{
cout<<pRoot->m_chValue<<" ";
return 1;
}
return PrintNodeAtLevel(pRoot->m_pLeft,level-1)+PrintNodeAtLevel(pRoot->m_pRight,level-1);
}
从上面的代码我们可以看出来,假如树的深度是depth,打印level层的节点,如果depth>=level,那么PrintNodeAtLevel返回值肯定大于0,如果depth<level,返回值是0。根据这个规律,我们可以写出如下递归的分层遍历。
void printNode(Node *root)
{
for(int level=0;;level++)
{
if(!PrintNodeAtLevel(root,level))
break;
cout<<endl;
}
}
上述算法每一层的访问都是重新从根节点开始的,知道访问完所有的层次。这样的做法,效率实在不高。下面我们就来讨论非递归的方法。
2.非递归层次遍历
要求:从上到下按层次访问该二叉树,每一层要求访问的顺序从左到右。
void PrintNodeByLevel(Node *pRoot)
{
if(pRoot==NULL)
{
return;
}
vector<Node*> vec;
vec.push_back(pRoot);
int cur=0,last=1;
while(cur<vec.size())
{
last=vec.size();
while(cur<last)
{
cout<<vec[cur]->m_chValue<<" ";
if(vec[cur]->m_pLeft)
vec.push_back(vec[cur]->m_pLeft);
if(vec[cur]->m_pRight)
vec.push_back(vec[cur]->m_pRight);
cur++;
}
cout<<endl;
}
}
编程之美最后说叶劲峰提出一种方法,我看了下比较给力,这个帮助我们更好理解层次遍历。学过数据结构的都学过图的广度遍历,用到了一个队列。二叉树可以使用这种方法,本题关键点是需要再每一层之间输出空行,这就要求我们识别一层的结束。叶劲峰的方法是,在一层结束之后,在队列插入一个结束信号(NULL指针),关键点是我们要想到什么时候插入这个NULL。应该在遇到NULL时,为什么如此?因为我们在队列中遇到NULL,表明本层的遍历已经结束,下层的所有节点已经入队,所以要将NU L入队。
代码:
void PrintNodeByLevel(Node* root) {
queue<Node*> Q;
Q.push(root);
Q.push(0);
do {
Node* node = Q.front();
Q.pop();
if (node) {
cout << node->data << " ";
if (node->pLeft)
Q.push(node->pLeft);
if (node->pRight)
Q.push(node->pRight);
}
else if (!Q.empty()) {
Q.push(0);
cout << endl;
}
} while (!Q.empty());
}
参考:http://www.cnblogs.com/miloyip/archive/2010/05/12/binary_tree_traversal.html