二叉树遍历和创建过程分析

一.遍历方式及实现

二叉树的遍历有三种方式:

对这样一个二叉树来说


1.先序遍历  A -> B -> C

2.中序遍历 B -> A -> C

3.后序遍历 B -> C -> A

任何二叉树都可以看作这样的一个简单的模型,只是图内的节点对应的子树各不相同。其中A为根节点。

对这样的二叉树,我们可以把遍历整个二叉树看作一个主问题,而遍历时遍历每一个子节点对应的子树的过程看作一个子问题。比如我要遍历这颗二叉树。我们先从A出发,遍历到B,此时B为A的左子树的根节点,我们要遍历B,相当于就是遍历这颗子树,同理,遍历B子树的时候我们会先到D,而D也同样是B的左子树的根节点,那么我们遍历D也就相当于是遍历D子树。

通过以上的过程,可以看到,很明显这是一个递归过程。要遍历A二叉树树,我们就要遍历B子树和C子树,遍历B和C子树的时候我们还要遍历B的子树和C的子树。也就是一个大问题(遍历主树)可以分解成若干个小问题。

既然知道是递归过程,代码就很好写出来了。

根据遍历顺序不同,

1.先序遍历

void bitree_tra(bitree* root) {
	if (root == 0)return;//子树空,相当于直接遍历完成
	cout << root->data;//按顺序输出节点

	bitree_tra(root->lch);//遍历左子树
	bitree_tra(root->rch);//遍历右子树
}

2.中序遍历

void bitree_tra(bitree* root) {
	if (root == 0)return;//子树空,相当于直接遍历完成
	

	bitree_tra(root->lch);//遍历左子树
    cout << root->data;//左子树遍历完返回双亲节点
	bitree_tra(root->rch);//遍历右子树
}

3.后序遍历

void bitree_tra(bitree* root) {
	if (root == 0)return;//子树空,相当于直接遍历完成
	
	bitree_tra(root->lch);//遍历左子树
	bitree_tra(root->rch);//遍历右子树
    cout << root->data;//左右子树遍历完返回双亲节点
}

如果把节点输出语句移除。可以看到三种遍历方式其实遍历思路是完全一样的,都是到节点然后遍历左子树和右子树。

代码非常简单。但过程并不清晰。

二.遍历过程和创建二叉树

我们以中序遍历为例来分析递归的过程理解遍历是如何实现的。

 对这颗二叉树

void bitree_tra(bitree* root) {
	if (root->lch == 0)return;//左子树空,相当于直接遍历完成
	

	bitree_tra(root->lch);//遍历左子树
    cout << root->data;//左子树遍历完返回双亲节点
	bitree_tra(root->rch);//遍历右子树
}

 按照这段代码,刚开始,我们会一直调用遍历左子树的函数bitree_tra(root->lch)。路径也就是A B D 当函数调用到D点时,我们开始遍历D的左子树,但是很明显D的左子树为空函数直接返回(也可以看作空树遍历完成),返回至D节点也是函数参数为D的函数。这样也就相当于遍历D的左子树过程已经结束了,因为是空树所以没有节点的输出。所以按照中序遍历顺序,现在轮到这颗子树的根节点,也就是D,按照程序输出D。接下来遍历右子树因为也是空,所以同左子树一样直接返回。

但是不同的是,右子树的遍历完成代表着按照中序遍历,这颗D子树已经完全遍历完成了。所以我们返回到D子树的双亲节点也就是B。因为D子树是B节点的左子树,也就是说B的左子树已经遍历完成,现在轮到输出B节点。  遍历完B 这个节点后轮到B的右子树,重复之前的操作直到主树A的右子树也遍历完,代表整棵树也遍历完。

按这个过程我们可以得到中序遍历这颗树 的节点访问顺序为 DBEAFC

再思考一下这个过程,我们可以知道,其实每个节点都被访问过了三次。

1.到递归调用函数的时候调用到了这个节点

2.左子树遍历完成函数返回的时候又回到了这个节点

3.右子树遍历完成后函数返回到了这个节点

我们只需要在这三次访问中挑出一次来输出,就是一种遍历方式。比如我们在1这种情况,也就是调用函数到了这个节点时就输出,然后再遍历左右子树,这就是先序遍历....

顺带一提,除了后序遍历,一个右子树的遍历完成代表两个二叉树的遍历完成,分别为这颗子树的遍历完成和,这颗子树的根节点的双亲节点的子树遍历完成

同样的过程,我们只需要在一个空树访问到每个节点时为其分配空间并赋值即可创建一颗二叉树。

以先序遍历创建为例:

void bitree_creat(bitree*& root) {
	int t;
	cin >> t;//每到一个节点时输入该节点的值,如果t==0,则表示这里是空节点
		if (t == 0)root = 0;
		else {
			root = new bitree;
			root->data = t;
			bitree_creat(root->lch);
			bitree_creat(root->rch);
		}
	
	return;
}

 比如这颗二叉树,本来只有一种遍历序列是无法确定一颗二叉树的,但是用空节点补全,则可以确切表示一颗二叉树

以数字表示节点#表示空节点,先序遍历顺序为 A B C # # D E # G # # F # # #

转为数字表示就是 1 2 3 0 0 4 5 0 6 0 0 7 0 0 0

调用之前的先序遍历函数 

理解完这个递归遍历的过程后,我们可以利用栈来迭代模拟这个过程。

三.迭代模拟

仍然以中序遍历为例。

主要思路跟递归一样,但是要用栈来模拟递归时调用函数会保留节点这一过程。

每访问一个节点就将其压入栈中,并从左子树开始遍历,左子树遍历结束模拟递归返回双亲节点这一过程将双亲节点弹出,并遍历右子树,循环这个过程直到主树的根节点被弹出且主树的右子树完成遍历

void bitree_tra(bitree* root) {
	bitree* T = root;
	bitree* temp = 0;//保留弹出的节点以便于操作其左右子树
	stack<bitree*>S;

	while (T || !S.empty()) {
		if (T) {
			S.push(T);//推入每一个节点模拟递归
			T = T->lch;//遍历左子树
		}
		else {//左子树遍历完
			temp = S.top();//temp此时为子树的双亲节点
			S.pop();//弹出节点
			cout << temp->data;
			T = temp->rch;
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值