在学习数据结构的时候,经常会考到用前中后序三者之二来重构二叉树或者得到另外一个未知的遍历顺序
往往对着图和数字我们可以轻松地把树画出来,但是如果要我们用代码实现它,又要怎么做呢?
其实过程是一样的,只要牢记前中后序的产生过程,逆向推导,加以严谨的程序控制就可以完成了。
- 前序遍历:也叫先根遍历,即先访问树的根节点,然后再依次递归访问左子树和右子树
- 中序遍历:也叫中根遍历,即先访问左子树,然后访问根节点,最后再访问右子树
假设我们的树长这个样子
那么就可以得到:
前序遍历:1 2 4 7 5 3 6 8
中序遍历:4 7 2 5 1 3 8 6
下一步用代码重构
定义如下数据结构:树的节点
struct BTreeNode
{
int m_ivalue;
BTreeNode* m_pleft;
BTreeNode* m_pright;
};
观察前序和中序的产生过程,不难发现:
- 前序的第一个元素就是根节点
- 而相应的根节点则在中序中间(不一定是正中间),中序中,根节点位置的左边都属于左子树,而右边则属于右子树
- 并且可以发现,前序中,是左子树先“扎堆”,然后是右子树”扎堆“,也即不会发生交叉现象
以我们的数据为例,从前序可以知道 1 就是根节点,从中序可以得知,4,7,2,5 都是在左子树中,1,3,8,6 则都在右子树中。
看看现在我们得到的有什么:
- 一个简单通过前序就能拿到的根节点
- 通过遍历可以从中序中划分出来的左子树节点群和右子树节点群
- 通过以上两步,可以确定左右子树各自的前中序,比如左子树,他的前序是 2,4,7,5 ,而其中序是 4,7,2,5
于是可以想到,可以用
递归 来重构树了!
从根节点出发,分别递归设置左右子树,当跳出递归时,整颗树都重构完毕
代码实现如下:
BTreeNode* build(int* preOrder, int* inOrder, int length)
{
if (preOrder == NULL || inOrder == NULL || length == 0) //前中序为空
return NULL;
else
return buildcore(preOrder, preOrder + length - 1, inOrder, inOrder + length - 1);
}
BTreeNode* buildcore(int* preOrderStart, int* preOrderEnd, int* inOrderStart, int* inOrderEnd)
{
BTreeNode* root = new BTreeNode(); //new 一个根节点,在递归过程中也可以称为子树根节点
root->m_ivalue = *preOrderStart;
root->m_pleft = root->m_pright = NULL; //初始化为NULL
if (preOrderStart == preOrderEnd){
if (inOrderEnd == inOrderStart && *preOrderStart == *inOrderStart)
return root;
else
throw std::exception("Invalid input");
}
int* rootInOrder = inOrderStart;
//在中序中遍历查找根所在的位置
while (rootInOrder != inOrderEnd && *rootInOrder != root->m_ivalue)
++rootInOrder;
if (rootInOrder == inOrderEnd && *rootInOrder != root->m_ivalue)
throw std::exception("Invalid input");
int leftLength = rootInOrder - inOrderStart;
int* leftPreOrderEnd = preOrderStart + leftLength;
//构建左子树
if (leftLength > 0){
root->m_pleft = buildcore(preOrderStart + 1, leftPreOrderEnd, inOrderStart, rootInOrder - 1);
}
//构建右子树
if (leftLength < inOrderEnd - inOrderStart){
root->m_pright = buildcore(leftPreOrderEnd + 1, preOrderEnd, rootInOrder + 1, inOrderEnd);
}
return root;
}
小结:要深刻理解前中后序的产生过程及其特点,这是构建树的关键。要理解用递归来处理树的问题,大而化小,分而治之