1.二叉树的遍历与重构
先前一直对二叉树的问题一直很害怕,经常涉及到二叉树的前中后遍历与重构问题,比如:
- 给定前序遍历序列和中序遍历序列,要你求出后序遍历序列,或者给定中序遍历序列和后序遍历序列,要你求出前序遍历序列;
- 其次,如果只给一个指向根节点的结点指针,要你利用递归法或者甚至迭代法完整无误的白板写出相应的代码可能就有些压力了;
- 再有甚者,给定前序遍历序列和中序遍历序列,要你重构该二叉树并返回指向根结点的结点指针。
1.1二叉树的遍历
由于二叉树问题涉及到结构体与结构体指针问题,凡是涉及到指针操作的都相对比较头疼。处理二叉树遍历与重构问题,其一个核心的知识点就是必须得明白前中后序遍历的原理。所谓的前中后遍历,无非就是根结点的访问次序问题。其中
- 前序遍历:根结点—>左孩子结点—>右孩子结点
- 中序遍历:左孩子结点—>根结点—>右孩子结点
- 后序遍历:左孩子结点—>右孩子结点—>根结点
首先,让我们来看看前中后序遍历。如前面所述,二叉树的遍历方法有两种,第一种是递归算法,第二种是迭代算法。
其中,递归算法的思想就是:从根结点开始,如果该结点指针非空,就一直递归(自身调用)下去,直到遇到空结点指针为止终止递归调用。Ps:递归算法,从形式结构上看是非常简洁的,但里面的递归调用涉及很多临时变量的,自身会利用栈资源对函数调用中断产生的变量进行压栈和出栈操作,如果递归的终止条件没有考虑好或者递归次数太大,很可能会导致深度递归导致有限的栈资源被消耗完,出现栈资源溢出。
//递归算法 伪代码 以前序遍历为例
function(pointer)
if(pointer!=nullptr)
pointer->value;
function(pointer->leftchild);
function(pointer->rightchild);
其次,迭代算法的主要思想也和递归类似,只是它不像递归算法在自身函数调用的时候自行对函数调用中断产生的临时变量进行压栈处理,可能得自己根据需要保留结点信息。下面以前序遍历的迭代算法为例,给出相应的代码。该博文最后给出了完整的测试用例!
void OutputPreOrder(TreeNode* root){
if(root==nullptr)
return;
stack<TreeNode*> s;//用于存储结点信息
s.push(root);
TreeNode* node=nullptr;
while(!s.empty()){
node=s.top();
s.pop();
cout<<node->val<<' ';
//如果右结点不为空,先将右结点压栈,然后再压左结点,等出栈的时候左结点先出栈。
if(node->right!=nullptr)
s.push(node->right);
if(node->left!=nullptr)
s.push(node->left);
}
}
1.2二叉树的重构
(以前序遍历和中序遍历重构二叉树为例)
要点:在二叉树的前序遍历序列中,第一个数字总是树的根节点的值。但在中序遍历序列中,根节点的值大都在序列的中间,以中序遍历序列根结点的位置为枢轴,位于其左边的结点全为根结点的左子树的结点,位于其右边的结点全为根结点的右子数的结点,将中序序列一分为二。其次,根据中序遍历序列划分的左半序列计算其结点个数,也同样可以将前序遍历序列划分成两部分。因此我们需要扫面中序遍历序列,确定根结点在中序遍历序列中的位置。如此这样,就将中序遍历序列和前序遍历序列划分为两个子序列,然后在子序列中再次递归调用,便能重构二叉树。 具体实现见如下相关代码段!
//****Code Block********
//main code block
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <stack>
#include <exception>
using namespace std;
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x):val(x),left(nullptr),right(nullptr){};
};
// functions declaration
struct TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> in);
struct TreeNode* ConstructTree(vector<int> &pre,vector<int> &in,int firstpre,int lastpre,int firstin,int lastin);
void OutputPreOrder(TreeNode* root);
void OutputInOrder(TreeNode* root);
void OutputPostOrder(TreeNode* root);
int _tmain(int argc, _TCHAR* argv[])
{
int prearr[]={1,2,4,7,3,5,6,8};
int inarr[]={4,7,2,1,5,3,8,6};
//using pre represents Preorder sequeues.
vector<int> pre(prearr,prearr+sizeof(prearr)/sizeof(int));
cout<<"The known preorder sequeues are: ";
for(auto iter=pre.begin();iter<pre.end();++iter)
cout<<*iter<<' ';
cout<<endl;
//using in represents Preorder sequeues.
vector<int> in(inarr,inarr+sizeof(inarr)/sizeof(int));
cout<<"The known inorder sequeues are: ";
for(auto iter=in.begin();iter<in.end();++iter)
cout<<*iter<<' ';
TreeNode* root=reConstructBinaryTree(pre,in);
cout<<"\nThe preorder sequeues are: ";
OutputPreOrder(root);
cout<<"\nThe inorder sequeues are: ";
OutputInOrder(root);
cout<<"\nThe postorder sequeues are: ";
OutputPostOrder(root);
return 0;
}
//reConstructBinaryTree code block
struct TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> in) {
if(pre.size()!=in.size())
return nullptr;
int len=pre.size();
return ConstructTree(pre,in,0,len-1,0,len-1);
}
struct TreeNode* ConstructTree(vector<int> &pre,vector<int> &in,int firstpre,int lastpre,int firstin,int lastin){
TreeNode* root=new TreeNode(pre[firstpre]);
if(firstpre==lastpre){
if(firstin==lastin && pre[firstpre]==in[firstin])
return root;
else
throw std::exception("Invalid parameter.");
}
int index=firstin;
while(index<=lastin && in[index]!=pre[firstpre])
index++;
if(index==lastin+1)
throw std::exception("Invalid parameter.");
//find the root node in the inorder sequeue
int length=index-firstin;
if(length>0){//construct left-child tree
root->left=ConstructTree(pre,in,firstpre+1,firstpre+length,firstin,firstin+length-1);
}
if(length<lastpre-firstpre){//construct right-child tree
root->right=ConstructTree(pre,in,firstpre+length+1,lastpre,firstin+length+1,lastin);
}
return root;
}
// preorder traversal code block
//递归法 前序遍历
void OutputPreOrder(TreeNode* root){
if(nullptr==root)
return;
if(root!=nullptr){
cout<<root->val<<" ";
OutputPreOrder(root->left);
OutputPreOrder(root->right);
}
}
//迭代法 前序遍历
void OutputPreOrder(TreeNode* root){
if(root==nullptr)
return;
stack<TreeNode*> s;
s.push(root);
TreeNode* node=nullptr;
while(!s.empty()){
node=s.top();
s.pop();
cout<<node->val<<' ';
if(node->right!=nullptr)
s.push(node->right);
if(node->left!=nullptr)
s.push(node->left);
}
}
// inorder traversal code block
//递归法 中序遍历
void OutputInOrder(TreeNode* root){
if(nullptr==root)
return;
if(root!=nullptr){
OutputInOrder(root->left);
cout<<root->val<<" ";
OutputInOrder(root->right);
}
}
//迭代法 中序遍历
void OutputInOrder(TreeNode* root){
if(nullptr==root)
return;
stack<TreeNode*> s;
TreeNode* node=root;
while(!s.empty()||node!=nullptr){
if(node!=nullptr){
s.push(node);
node=node->left;
}
else{
node=s.top();
s.pop();
cout<<node->val<<' ';
node=node->right;
}
}
}
// postorder traversal code block
//递归法 后序遍历
void OutputPostOrder(TreeNode* root){
if(nullptr==root)
return;
if(root!=nullptr){
OutputPostOrder(root->left);
OutputPostOrder(root->right);
cout<<root->val<<" ";
}
}
//迭代法 后序遍历
void OutputPostOrder(TreeNode* root){
if(nullptr==root)
return;
stack<TreeNode*> s;
TreeNode* node=root,*pre;
do{
while(node!=nullptr){
s.push(node);
node=node->left;
}
pre=nullptr;
while(!s.empty()){
node=s.top();
s.pop();
if(node->right==pre){
cout<<node->val<<' ';
pre=node;//保存访问过的结点
}
else{
s.push(node);
node=node->right;
break;
}
}
}while(!s.empty());
}