编程之美系列之二叉树的分层遍历

本文介绍了一种优雅的方法来实现二叉树的层次遍历,即按层打印二叉树的所有节点。通过使用队列并巧妙地控制节点的进出队顺序,避免了额外的空间开销,提供了两种实现方式:一种是使用固定大小的数组模拟队列,另一种是使用动态扩展的vector。

关于二叉树的遍历,大家肯定都不会陌生。当然,这种题目很多人都会做,但是你们心中的解题办法是不是够漂亮呢?
首先,这里需要用到的数据结构的队列,这应该没有什么疑问,先进先出嘛。子节点在父节点之后,而用队列,子节点是通过父节点入队列,所以在父节点之后,OK~符合标准!先来说说大致的一些解题思路。
1、在NODE的定义中定义一个flag标记,用来记录该节点属于哪一层,然后如果当前节点的层数大于上一个节点的层数,那就在打印这个节点之前打印一个换行符。但是这样做有什么不好呢?在NODE的定义中加入flag标记,这样会使我们定义的数据类型过于庞大,这肯定是不好的。
2、当然如果不能在NODE里面定义一个flag标记,也可以,那就利用一个辅助的队列来表明当前节点所属的层数。其实上面这两种思想在面试100题系列之11在树中找到符合条件的路径里面有反映。当然,在那里,用辅助栈的思想是比较好的,但是这里,情况又不是完全一样,有没有可能说除了一个队列之外,不需要其他的辅助内存呢??可以做到么?答案是肯定的。
3、那如果不用辅助内存怎么做?初始的状态只有根节点会在队列里面,OK,其余所有的节点都是通过根节点进来的,而且每次遍历其实只要控制得好,是可以让节点一层一层进来的,怎么做?
用一个Begin记下这一层的开始,End记下这一层的结尾,OK,通过Begin和End之间的节点进入队列的一定是下一层的节点。当Begin和End之间的节点访问完之后,打印一个换行符,进入下一层遍历,将Begin指向上一次的End+1,End指向新的NewEnd。然后依次循环下去。初始时刻,那就只有根节点在队列中,Begin=End=NewEnd=0。
参考代码:

//用数组来模拟队列
void PrintTreeByLevelWithArr(NODE *pRoot)
{
	if(!pRoot)
		return;
	NODE *Queue[N];//N为预设的大小
	int Begin = 0;
	int End = 0;
	int NewEnd = 0;
	Queue[0] = pRoot;
	while(Begin <= End)
	{
		while(Begin <= End)
		{
			printf("%d ", Queue[Begin]->data);
			if(Queue[Begin]->Left)
				Queue[++NewEnd] = Queue[Begin]->Left;
			if(Queue[End]->Right)
				Queue[++NewEnd] = Queue[Begin]->Right;
			++Begin;
		}
		printf("\n");
		End = NewEnd;
	}
}
//用vector来模拟队列
void PrintTreeByLevelWithVec(NODE *pRoot)
{
	if(!pRoot)
		return;
	//这里用vector来代替数组,利用其动态扩展的属性
	vector<NODE *> vec;
	int Begin = 0;
	int End = 1;
	vec.push_back(pRoot);
	while(Begin < End)
	{
		while(Begin < End)
		{
			printf("%d ", vec[Begin]->data);
			if(vec[Begin]->Left)
				vec.push_back(vec[Begin]->Left);
			if(vec[Begin]->Right)
				vec.push_back(vec[Begin]->Right);
			++Begin;
		}
		End = vec.size();
		printf("\n");
	}
}
最后,按照惯例,给出一些数据结构、变量的定义,以及main函数等:
#include<stdio.h>
#include<vector>//vector所在的库文件
using namespace std;
struct NODE
{
	int data;
	NODE *Left;
	NODE *Right;
};
const int N = 30;
NODE node[N];
int top;
//往二叉查找树里面加节点
void Add(NODE *root, int v)
{
	if(!root)
		return;
	if(root->data < v)
	{
		if(root->Right)
			Add(root->Right, v);
		else
		{
			root->Right = &node[++top];
			node[top].data = v;
		}
	}
	else if(root->data > v)
	{
		if(root->Left)
			Add(root->Left, v);
		else
		{
			root->Left = &node[++top];
			node[top].data = v;
		}
	}
	else
		printf("重复加入节点%d\n", v);	
}
int main()
{
	int n,i,v;
	while(scanf("%d", &n) != EOF)
	{
		memset(node, 0, sizeof(node));
		top = 0;//top是node数组的顶
		scanf("%d", &v);
		node[0].data = v;
		for(i = 1; i < n; ++i)
		{
			scanf("%d", &v);
			Add(&node[0], v);
		}
		PrintTreeByLevelWithArr(&node[0]);
		PrintTreeByLevelWithVec(&node[0]);
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值