由二叉树重建所引出的算法

前几天在sicily做到一道关于二叉树重建的问题,引出了许许多多的思考。

这道题的链接在:http://soj.me/1935 其实这道题不算太难,之前在数据结构课上,做过类似的,不过要将二叉树重建过来。

代码的实现如下:

#include <iostream>

#include <queue>

#include <string>

using namespace std;

template <typename T> struct BinaryNode{

  T elem;

  BinaryNode *left;

  BinaryNode * right;

  BinaryNode(T d, BinaryNode *l=NULL, BinaryNode *r=NULL):elem(d),left(l),right(r){};

};



BinaryNode<char>*  buildTree(const string &preorder, const string &inorder)

{

    BinaryNode<char> * root;

    string lp,li,rp,ri;

    int i,len,rlen;

    if(inorder.size() == 0 && preorder.size() ==0 ) 

        return NULL;

    len = preorder.size();

    char s = preorder[0];

    for(i = 0 ; i < len; i++)

    {

        if(inorder[i] == s)

            break;

    }

    rlen = len -1 -i;

    root = new BinaryNode<char>(s);

    lp = preorder.substr(1,i);

    li = inorder.substr(0,i);

    rp = preorder.substr(i+1,rlen); 

    ri = inorder.substr(i+1,rlen);

    root->left = buildTree(lp,li);

    root->right = buildTree(rp,ri);

    return root;

} 



void pro(BinaryNode<char> * root)

{

	if(root == NULL)

		;

	else

	{

		pro(root->left);

		pro(root->right);

		cout << root->elem ;

	}

}



int main()

{

	int n;

	cin >> n;

	while(n--)

	{

		string pre,in;

		cin >> pre >> in;

		BinaryNode<char> * root = buildTree(pre,in);

		level(root);

		cout << endl;

	}

	return 0;

}

 所以我在想:既然是用到递归的方法,能否不用建树的方法来完成这一道题呢?于是经过上网查阅资料,给我找到了这个算法。

关于这个算法,我真的不是很了解:之前并没有看过类似的算法,所以它到底是作者自创的呢?还是有一定的背景呢?

我不清楚。

这个算法大概如下:

Example:

ABDGCEF

DGBAECF

sample output:

GDBEFCA

用一个order数组存中序遍历中的字符在后序遍历中的顺序,从大到小。比如sample input中所得的order为:5 6 4 0 3 1 2。

直接用上面的例子说明算法过程:

order=0 0 0 0 0 0 0

对于str1中的A,找出它在str2中的位置为4,它左右order的值都和4位置的order的值相同,说明它有左、右子树

改变order的值为:

2 2 2 0 1 1 1

对于str1中的B,找出它在str2中的位置3,它左order的值和当前order值相同,说明它有左子树,

改变order的值为:

3 3 2 0 1 1 1

对于str1中的D,找出它在str2中的位置1,改变order的值为:

3 4 2 0 1 1 1

对于str1中的G,找出它在str2中的位置2,改变order的值为:

3 4 2 0 1 1 1

对于str1中的C,找出它在str2中的位置6,改变order的值为:

5 6 4 0 3 1 2

....

处理完后就得到order的值为5 6 4 0 3 1 2,恰好是后序遍历中str2的字符被反问到的顺序(从大到小)。

代码直接实现如下:

#include <iostream>

#include <string>

using namespace std;



const int maxn=100;



int find(char c,char str[])

{

    int len=strlen(str);

    for (int i=0;i<len;i++)

        if (c==str[i]) return i;

    return len;

}



int main()

{

    char str1[maxn],str2[maxn],str[maxn];

    cin>>str1>>str2;//输入前、中序遍历

    int len=strlen(str1);

    if (len!=strlen(str2)) return -1;

    int order[maxn]={0};//order初始化为0

    for (int i=0;i<len;i++)

    {

        int k=find(str1[i],str2);//找到前序第i个字符在中序字符中的位置

        int t=0;

        if (k>0 && order[k-1]==order[k]) t++;//有左子树

        if (k<len-1 && order[k+1]==order[k]) t++;//有右子树



        for (int j=0;j<len;j++) if (order[j]>order[k]) order[j]+=t;//对于上面一层的加深层数



        if (t==2)

        {

            for (int j=k-1;j>=0;j--) if (order[j]>=order[k]) order[j]+=t; else break;//左子树被访问顺序会比右子树快1,所以下面order[j]+=t-1;

            for (int j=k+1;j<len;j++) if (order[j]>=order[k]) order[j]+=t-1; else break;

            continue;

        }



        if (t==1)

        {

            if (k>0 && order[k-1]==order[k])//有左子树

                for (int j=k-1;j>=0;j--) if (order[k]==order[j]) order[j]+=t; else break;

            if (k<len-1 && order[k+1]==order[k])//有右子树

                for (int j=k+1;j<len;j++) if (order[k]==order[j]) order[j]+=t; else break;

        }

    }



    for (int i=0;i<len;i++)//映射中序为后序

        str[len-order[i]-1]=str2[i];



    str[len]=0;

    cout<<str<<endl;//输出

    return 0;

}





 

你可能感兴趣的:(二叉树)