天天看点

根据二叉树的前序和中序排列创建二叉树(使用vector实现)

 二叉树的前序排列与中序排列可以确定唯一的一颗二叉树。

基本原理:二叉树前序排列是“根结点——>左子树——>右子树”,中序排列是“左子树——根结点——右子树”。如下图所示二叉树:

根据二叉树的前序和中序排列创建二叉树(使用vector实现)
根据二叉树的前序和中序排列创建二叉树(使用vector实现)

数字,前序为:1,2,4,5,6,3,7,8,9; 中序为:4,2,5,6,1,7,3,9,8。

字符,前序为:A,B,C,D,E,F,G; 中序为:C,B,E,D,A,F,G。

前序和中序的关联是:前序依次始终可以看作是二叉树的根结点或子树(包括左、右子树)的根结点。所以每个前序的结点,都可以把中序的结点分为:左子树和右子树两部分。如前例中的数字:1为根结点时,在中序中:4,2,5,6即为左子树部分,7,3,9,8即为右子树部分。然后以此类推,当2为子树根结点时,在中序里,4为左子树,5,6为右子树部分……

递归函数由此设计:函数中传入的参数为:前序vector表,中序vector表,以及根据此时的根结点创建的BiTNode结点指针。随着递归的展开,前序表和中序表中的元素因不断提取作为新的子树的根结点,因而也不断减少,直到提取完毕,前序表为空就退出递归了。

以数字为例,先看代码:

template<typename T>
void createBTree(vector<T> preOrder, vector<T> inOrder, BiTNode<T> *root) {
	//如果preOrder为空就退出
	if (preOrder.size()) {
		//根(或子树的根)节点已经创建了BiTNode节点(即参数root),所以在前序里不再需要,删除。
		preOrder.erase(preOrder.begin());
		//删除第一个元素后,如果preOrder为空就退出
		if (preOrder.size()) {
			//preOrder第一个值总是可以看作是子树的根结点。
			T rootVal = root->data;
			
			vector<T> leftPreOrder, leftInOrder, rightPreOrder, rightInOrder;


			//根据根结点值,把中序的元素分成左子树和右子树两部分的节点
			//并分别把左子树和右子树又各自分为:左子树的前序和中序,右子树的前序和中序两部分。

			//leftInOrder,rightInOrder
			
			int size = inOrder.size();
			int i;
			for (i = 0;i < size;i++) {
				if (inOrder[i] != rootVal)
					//根据根结点(或子树的根结点)以及中序(InOrder)得到左子树的新的中序(leftInOrder)。
					leftInOrder.push_back(inOrder[i]);
				else
					break;

			}
			for (i++;i < size;i++) {
				//剩下的就是右子树的新的中序(rightInOrder)。
				rightInOrder.push_back(inOrder[i]);
			}
			//leftPreOrder,rightPreOrder
			size = preOrder.size();
			for (i = 0;i < size;i++) {
				//根据左子树新的中序在之前的前序中查找,从而创建左子树新的前序
				if (std::find(leftInOrder.begin(), leftInOrder.end(), preOrder[i]) != leftInOrder.end())
					leftPreOrder.push_back(preOrder[i]);
				else
					break;

			}

			for (;i < size;i++) {
				//剩下的就是右子树新的前序
				rightPreOrder.push_back(preOrder[i]);
			}

			//leftPreOrder左子树递归循环
			if (leftPreOrder.size()) {
				//创建左子树的根结点,并建立其与父节点之间的关系。
				root->Lchild = new BiTNode<T>;
				root->Lchild->data = leftPreOrder[0];
				createBTree(leftPreOrder, leftInOrder, root->Lchild);
			}
			//rightPreOrder右子树递归循环
			if (rightPreOrder.size()) {
				//创建右子树的根结点,并建立其与父节点之间的关系。
				root->Rchild = new BiTNode<T>;
				root->Rchild->data = rightPreOrder[0];
				createBTree(rightPreOrder, rightInOrder, root->Rchild);
			}




		}



	}
	
	
}
           

根据以上代码, 以数字为例,详述递归函数展开顺序:

首先,函数第一次进入:传入的vector表前序为原表的值:{ 1,2,4,5,6,3,7,8,9 },中序也没有改变,为原表:{ 4,2,5,6,1,7,3,9,8 }。参数root为创建的整个树的根结点对象,其data值为:前序第一个值:1。

函数进入后,做了以下几件事:

(1)删除前序表的第一个值1,因为它已经存在于root根结点对象中,在下一递归循环中不需要。

(2)根据根结点1从传入的中序表中找出左子树新的中序表元素:{ 4,2,5,6},把它放进leftInOrder中。同理:右子树的新的中序表元素{7,3,9,8 },把它放进rightInOrder表中。

(3)根据新的左子树的中序表,从前序表中找出对应的元素,组成新的前序表:{2,4,5,6},放进leftPreOrder表中。同理:右子树新的前序表为:{3,7,8,9},放进rightPreOrder中。

(4)进入新的递归循环或退出循环:如果新建立的左子树的前序表为空,就退出左子树递归循环。不为空,以新的前序表,中序表,子树根结点对象 为参数,进入新的循环。同理:如果新建立的右子树的前序表为空,就退出右子树递归循环。不为空,就以新的前序表,中序表,右子树根结点进入递归循环。

……

后续展开请自行推导了。

以下为原码:

#include<iostream>
#include<vector>
#include "BiTNode.h"
#include "BinaryTree.h"

using namespace std;

//递归模板函数(根据二叉树的前序和中序排列创建唯一的二叉树
template<typename T>
void createBTree(vector<T> preOrder, vector<T> inOrder,BiTNode<T> *root);

int main() {

	//设定二叉树节点值的类型
	using elemType = int;
	//二叉树的前序:元素值要保持不等,否则程序查找时会出错。
	vector<elemType> preOrderVector = { 1,2,4,5,6,3,7,8,9 }; //char型也可以。{ 'A','B','C','D','E','F','G' };
	//二叉树的中序:元素值要保持不等,否则程序查找时会出错。
	vector<elemType> inOrderVector = { 4,2,5,6,1,7,3,9,8 };//char型也可以。{ 'C','B','E','D','A','F','G' };

	int size= preOrderVector.size();
	if (size) {
		//创建二叉树根结点BiTNode
		elemType root = preOrderVector[0];
		BiTNode<elemType> *rootBiTNode = new BiTNode<elemType>;
		rootBiTNode->data = root;
		createBTree(preOrderVector, inOrderVector, rootBiTNode);

		BinaryTree<elemType> tree(rootBiTNode);
		tree.DisplayBinaryTree();
	}
	return 0;
}

template<typename T>
void createBTree(vector<T> preOrder, vector<T> inOrder, BiTNode<T> *root) {
	//如果preOrder为空就退出
	if (preOrder.size()) {
		//根(或子树的根)节点已经创建了BiTNode节点(即参数root),所以在前序里不再需要,删除。
		preOrder.erase(preOrder.begin());
		//删除第一个元素后,如果preOrder为空就退出
		if (preOrder.size()) {
			//preOrder第一个值总是可以看作是子树的根结点。
			T rootVal = root->data;
			
			vector<T> leftPreOrder, leftInOrder, rightPreOrder, rightInOrder;


			//根据根结点值,把中序的元素分成左子树和右子树两部分的节点
			//并分别把左子树和右子树又各自分为:左子树的前序和中序,右子树的前序和中序两部分。

			//leftInOrder,rightInOrder
			
			int size = inOrder.size();
			int i;
			for (i = 0;i < size;i++) {
				if (inOrder[i] != rootVal)
					//根据根结点(或子树的根结点)以及中序(InOrder)得到左子树的新的中序(leftInOrder)。
					leftInOrder.push_back(inOrder[i]);
				else
					break;

			}
			for (i++;i < size;i++) {
				//剩下的就是右子树的新的中序(rightInOrder)。
				rightInOrder.push_back(inOrder[i]);
			}
			//leftPreOrder,rightPreOrder
			size = preOrder.size();
			for (i = 0;i < size;i++) {
				//根据左子树新的中序在之前的前序中查找,从而创建左子树新的前序
				if (std::find(leftInOrder.begin(), leftInOrder.end(), preOrder[i]) != leftInOrder.end())
					leftPreOrder.push_back(preOrder[i]);
				else
					break;

			}

			for (;i < size;i++) {
				//剩下的就是右子树新的前序
				rightPreOrder.push_back(preOrder[i]);
			}

			//leftPreOrder左子树递归循环
			if (leftPreOrder.size()) {
				//创建左子树的根结点,并建立其与父节点之间的关系。
				root->Lchild = new BiTNode<T>;
				root->Lchild->data = leftPreOrder[0];
				createBTree(leftPreOrder, leftInOrder, root->Lchild);
			}
			//rightPreOrder右子树递归循环
			if (rightPreOrder.size()) {
				//创建右子树的根结点,并建立其与父节点之间的关系。
				root->Rchild = new BiTNode<T>;
				root->Rchild->data = rightPreOrder[0];
				createBTree(rightPreOrder, rightInOrder, root->Rchild);
			}




		}



	}
	
	
}


           
BiTNode.h

#pragma once

template<typename T>
struct BiTNode{
	BiTNode<T> *Lchild=NULL, *Rchild=NULL,*parent=NULL;
	T data;
	int depth=0;
	int order = 0;

	
};


           

 需要图形显示二叉树的话,需要用自定义的BinaryTree.h及其它自定义类等。代码请见本人其它博客文章。不需要显示的话,就删除main()里面的这两句代码及对应头文件:

BinaryTree<elemType> tree(rootBiTNode);
tree.DisplayBinaryTree();
           
c++
上一篇: 2018年4月

继续阅读