【二叉树构建与遍历3】先序遍历+后序遍历构建一个满二叉树并输出中序遍历 C++实现

注意:根据先序遍历与后序遍历 只有在满二叉树 的情况下才能确定一个唯一的树。这里介绍的是根据先序遍历+后序遍历构建一个满二叉树并输出中序遍历顺序。

思路:

先来一个例子:

先序遍历序列为:FDXEABG

后序遍历序列为:XEDBGAF

要根据先序序列和后序序列确定这个满二叉树,通用的步骤为:

1.根据先序序列的第一位或后序序列的最后一位确定这棵树的根root,但是不能用于确定它的左子树与右子树;

2.根据后序序列的倒数第二位确定右子树的根,这里命名为rRoot,这是用于定位的,用于确定根root的左子树与右子树;

3.在先序序列中找到右子树的根rRoot所在的位置,右子树的根rRoot的左边(除首位)就是该树的左子树的节点,右子树的根rRoot以及右边就是根root的右子树的节点;

4.根据左子树节点和右子树节点在后序序列中分别找到对应的子串;

5.对4中找到的两个子串分别重复1 2 3 4步,左子树节点用于构建左子树,右子树节点用于构建右子树,直到所有的节点都用于构建这棵树。

示例:

根据以上的案例走一遍:

1.由先序遍历序列为:FDXEABG可知,树root的根节点为F;

2.在后序遍历序列XEDBGAF中找到右子树的跟,这里命名为rRoot,也就是倒数第二个节点A

3.在先序遍历序列FDXEABG找到根root的右子树的跟A的索引为4,左边DXE(除首位)就是该树的左子树的节点,根A以及右边的ABG就是根root的右子树的节点;

4.根据树的左子树节点DXE和右子树节点ABG在后序序列中分别找到对应的子串,分别为:XED和BGA;

4.对XED和BGA(加上定位用的根节点A)分别重复步骤1 2 3,XED用于构建左子树,BGA用于构建右子树,直到所有的节点都用于构建树。

每执行完第一轮步骤1 2 3,所用的根节点已经用于构建树了,后续就不用再考虑了。例如,执行完第一轮步骤1 2 3,根节点F已经用过了,后续就不用再考虑了。

分析:

相信思路都很明确,步骤大概也明白了,接下来代码实现中一个非常注重细节的地方就是递归构建左子树和右子树的部分,再详细说就是在递归调用构建树的函数中,我们要传入的两个参数应该怎么确定。

本次主要介绍利用先序遍历序列和后序遍历序列构建一个二叉树并输出中序遍历的方法,我们在递归调用构建树的函数中,我们要传入的两个参数当然就是子树的先序遍历序列和后序遍历序列。创建左子树时就传入左子树的先序遍历序列和后序遍历序列,创建右子树时就传入右子树的先序遍历序列和后序遍历序列。

以下根据以上案例详细分析:对于:

cpp 复制代码
索 引:0123456

先序遍历序列为:FDXEABG

中序遍历序列为:XEDBGAF

将以上两个遍历序列分别命名为字符串s1,s2,即s1 = "FDXEABG", s2 = "XEDBGAF"。

根据s1可知树的根root为F,根据s2可知树右子树的根rRoot为A,在s1中寻找到A的索引(定义为pos)为4。

根据s1可得左子树包括的字符有:DXE(由s1.substr(1, pos-1)获得),右子树包括的字符有:ABG(由s2.substr(pos)获得),

在s2中对应的字符串分别为:XED(由s2.substr(0, pos-1)获得)和BGA(由s2.substr(pos-1, str1.size() - pos)获得)。

根据以上获得的四个参数,可以递归创建左子树和右子树。

源代码:

cpp 复制代码
//根据先序遍历和后序遍历确定一个二叉树,但必须是满二叉树
// 二叉树节点结构定义
struct TreeNode {
	char data;
	TreeNode* leftChild;
	TreeNode* rightChild;
	TreeNode(char c) : data(c), leftChild(NULL), rightChild(NULL) {}
};

// 根据先序遍历和后序遍历构建满二叉树
TreeNode* Build(string str1, string str2) {
	if (str1.size() == 0) {
		return NULL;
	}
	// 取先序遍历的第一个字符作为根节点
	//根节点可以根据先序遍历的第一个字符和后序遍历的最后一个字符直接获得
	TreeNode* root = new TreeNode(str1[0]);
	//当先序遍历中只剩下一个节点时,已经在前面的创建子树的作为根节点使用过了,就不需要再做处理了
	if (str1.size() > 1) {
		// 在后序遍历中找到左子树根节点的位置
		char c = str2[str2.size() - 2];
		// 在先序遍历中找到根节点的位置
		int pos = str1.find(c);
		// 递归构建左子树
		root->leftChild = Build(str1.substr(1, pos - 1), str2.substr(0, pos - 1));
		// 递归构建右子树
		root->rightChild = Build(str1.substr(pos), str2.substr(pos - 1, str1.size() - pos));
	}

	return root;
}

//中序遍历
void InOrder(TreeNode* root) {
	if (root == NULL) {
		return;
	}
	// 先遍历左子树
	InOrder(root->leftChild);
	//输出当前节点的值
	cout << root->data;
	// 再遍历右子树
	InOrder(root->rightChild);

	return;
}

int main()
{
	string s1, s2;
	while (getline(cin, s1)) {
		getline(cin, s2);
		// 根据先序遍历和后序遍历递归构建满二叉树
		TreeNode* root = Build(s1, s2);
		// 中序遍历该满二叉树
		cout << "中序遍历结果为:";
		InOrder(root);
		cout << endl;
	}

	return 0;
}

示例运行结果:

相关推荐
CYBEREXP2008几秒前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos
ZSYP-S19 分钟前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos22 分钟前
c++------------------函数
开发语言·c++
yuanbenshidiaos26 分钟前
C++----------函数的调用机制
java·c++·算法
唐叔在学习30 分钟前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA1 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
tianmu_sama1 小时前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
chengooooooo1 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc1 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
羚羊角uou1 小时前
【C++】优先级队列以及仿函数
开发语言·c++