关闭

白话篇:利用二叉树先序/中序/后序确定二叉树求法分析

标签: 数据结构 树
2026人阅读 评论(0) 收藏 举报
分类:

前篇:

二叉树的遍历:

二叉树的遍历是指按照一定次序访问二叉树中所有的节点,并且每个节点仅仅能够访问一次。这也是二叉树最基本的运算。

常用的3钟递归遍历方式:

1.先序遍历,过程:

(1)访问根节点

(2)访问左子树

(3)访问右子树

2.中序遍历,过程:

(1)访问左子树

(2)访问根节点

(3)访问右子树

3.后序遍历:
(1)访问左子树

(2)访问右子树

(3)访问根节点

为了能够更好的理解到这三种遍历方式,我们画图来模拟一下:

对于一颗二叉树:



1.先序遍历结果:ABDGCEF

2.中序遍历结果:DGBAECF

3.后序遍历结果:GDBEFCA


中篇:

1.如何利用先序和中序确定一颗二叉树

首先我们先证明一下可行性:对于任何n(n>0)个不同节点的二叉树,都可以由它的中序序列和先序序列唯一确定:

采用数学归纳法证明:

当n=0时,二叉树为空,结论正确。

假设:节点数小于n的任何二叉树,都可以由它的中序和先序序列唯一确定。

若一棵树具有n个节点,其先序序列为:a0,a1,a2……an-1,中序序列:b0,b1,b2……bn-1。

由先序遍历特性可知 a0必为二叉树的根节点。而且a0必然在中序序列中出现,也就是说中序序列中存在一个节点bk,bk为根节点a0.

由于bk是根节点,根据中序遍历特性,可以知道在中序序列中b0b1……bk-1必然为跟节点bk的左子树的中序序列。bk+1……bn-1必然为根节点bk的右子树的中序遍历。(因为中序遍历先遍历左子树,然后是根节点,最后是右子树)。即:bk的左子树有k个节点,右子树有n-k-1个节点。

根据先序遍历的特性,那么在先序序列中,a0后面的k个节点a1……ak就是左子树的先序遍历,ak+1……an-1就是右子树的先序序列。

以上过程如图:


由此归纳假设:子先序序列a1……ak,和子中序序列b0……bk-1可以唯一的确定根节点a0的左子树,二先序序列的ak+1……an-1和子中序序列bk+1…bn-1可以唯一的确定根节点a0的右子树(因为这里我们虽然只是确定了一步,但是可以通过递归此过程一步步划分,最终确定此二叉树毕竟操作都是相同的)。

本质:先序序列的作用是确定一颗二叉树的根节点(其第一个元素为根节点),中序序列的作用是确定左右子树的中序序列(包括各自的节点个数),进而可以确定左右子树的先序序列。可以将其理解为一个递归的过程因为这些步骤的操作都是重复性的。只要第一步执行了,后面的操作跟着执行此步骤即可得到最后的结果。

模拟:先序序列:ABDGCEF,中序序列:DGBAECF,则构造过程:


代码实现:

typedef struct T       //节点
{ 
	char data;
	struct T  *l,*r;
}T;


T *Change(char *pre, char *in, int n)
{
	if (n <= 0)
	{
		return NULL;    //如果n<=0,二叉树为空
	}
	T *Tree;
	Tree = (T *)malloc(sizeof(T)); //为节点申请空间
	Tree->data = *pre;          //pre为根节点
	int k;
	char *p;
	for (p = in; p < in + n; p++) //在中序中寻找根节点所在的位置
	{
		if (*p == *pre)
			break;
	}
	k = p - in;                     //然后得到根节点的左子树有多少个节点
	Tree->l = Change(pre + 1, in, k); //递归构造左子树(左子树的父节点都是按照顺序的,只需要pre+1即可,k为左子树的个数)
	Tree->r = Change(pre + k + 1, p + 1, n - k - 1);递归构造右子树(右子树的父节点为pre+k+1,中序的右子树为根节点+1,即:p+1,右子树的个数为n-1-k)
	return Tree;  返回构造的二叉树的根。
}

2.中序和后序确定二叉树的构造。

首先我们先证明一下可行性:对于任何n(n>0)个不同节点的二叉树,都可以由它的中序序列和后序序列唯一确定:

采用数学归纳法证明:

当n=0时,二叉树为空,结论正确。

假设:节点数小于n的任何二叉树,都可以由它的中序和后序序列唯一确定。

若一棵树具有n个节点,其后序序列为:a0,a1,a2……an-1,中序序列:b0,b1,b2……bn-1。

由后序遍历特性可知 an-1必为二叉树的根节点。而且an-1必然在中序序列中出现,也就是说中序序列中存在一个节点bk,bk为根节点an-1.

由于bk是根节点,根据中序遍历特性,可以知道在中序序列中b0b1……bk-1必然为跟节点bk的左子树的中序序列。bk+1……bn-1必然为根节点bk的右子树的中序遍历。(因为中序遍历先遍历左子树,然后是根节点,最后是右子树)。即:bk的左子树有k个节点,右子树有n-k-1个节点。

根据后序遍历的特性,那么在后序序列中,an-1前面的k个节点ak……an-2就是右子树的后序遍历,a0……ak-1就是左子树的后序序列。


由此归纳假设:子后序序列a0……ak-1,和子中序序列b0……bk-1可以唯一的确定根节点an-1的左子树,子后序序列的ak……an-1和子中序序列bk+1…bn-1可以唯一的确定根节点an-1的右子树(因为这里我们虽然只是确定了一步,但是可以通过递归此过程一步步划分,最终确定此二叉树毕竟操作都是相同的)。

模拟:后序序列:DGBAECF,中序序列:DGBAECF,则构造过程:





代码实现:


typedef struct T
{
	char data;
	struct T  *l,*r;
}T;

T *Change(char *post,char *in, int n)   //后序,中序
{
	if (n <= 0)
	{
		return NULL;   //节点小于0,树为空
	}
	T *Tree;
	Tree = (T *)malloc(sizeof(T)); //申请空间
	Tree->data = *(post + n - 1);    //根节点为后序遍历的最后一个节点
	int k;
	char *p;
	for (p = in; p < in + n; p++)  // 在中序序列中找到根节点的位置
	{
		if (*p == *(post + n - 1))
			break;
	}
	k = p - in;                        //计算左子树有多少个
	Tree->l = Change(post, in, k);      //递归创建左子树
	Tree->r = Change(post + k,p + 1,n-k-1); //递归创建右子树
	return Tree;  //返回根节点
}

以上代码亲测可行。

后篇:

为什么先序和后序不能唯一的确定一颗二叉树?

先序中序或者中序后序都具有共同点,那就是找到根节点,区分左右子树。在本质上来说,就是将父节点和子节点进行分离,左右子树分离。但是对于前序和后序来说,他永远只能找到的是开始时的根节点。然而在下步的操作中会有多种选择,最后造成的结果就是得到的二叉树不唯一。

我们也可以根据定义上来解释:

后序遍历:
(1)访问左子树

(2)访问右子树

(3)访问根节点

先序遍历,

(1)访问根节点

(2)访问左子树

(3)访问右子树


那么我们可以根据定义找到根节点,但是对于后序,先访问左子树后访问右子树,先序遍历访问完根节点后,会访问做左子树,再访问右子树。他们都是访问完左子树后访问右子树。这就造成了左子树和右子树的分离无法明确。或者说父子分离出现了问题。

中序遍历,过程:

(1)访问左子树

(2)访问根节点

(3)访问右子树


而对于中序先序或者中序后序来说,中序在访问完左子树后,访问了根节点,然后访问了右子树,那么我们就可以根据中序先序或者中序后序得到一颗确定的二叉树。可以完全正确的对父子节点或者左右子树进行分离。


1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:164023次
    • 积分:2861
    • 等级:
    • 排名:第12945名
    • 原创:123篇
    • 转载:11篇
    • 译文:0篇
    • 评论:26条
    博客专栏
    最新评论