算法题目中有很多关于二叉树遍历的题目,以下就简要说一下可能的情况:

    1、已知先序和中序,求后序。比如先序1 2 3 4 6 7 5,中序2 1 6 4 7 3 5。主要步骤有:1、求当前父节点,该节点是当前先序序列的第一个。2、在中序节点中找出该父节点的位置(i,从0开始计数)。3、获得左右孩纸节点,中序的序列左右孩纸(0到i-1),(i,len-1),先序左右孩纸就是(1,i)(i+1,len-1)。整理出:左孩纸先序节点(0,i-1),中序序列节点(1,i),右孩纸的先序(i,len-1),中序是(i+1,len-1)。4分别对左右孩纸进行1步骤。

#include<iostream>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<iterator>
#include<algorithm>
#include<cstring>
using namespace std;
struct Node
{
    int key;
    Node* left;
    Node* right;
    Node(){left=right=NULL;}
};
void post_travel(Node* root)
{
    if(root==NULL)
    {
        return;
    }
    post_travel(root->left);
    post_travel(root->right);
    printf("%d ",root->key);
}
Node* CreateTree(int* pre,int* inorder,int len)
{
    if(len<=0) return NULL;
    Node* node = new Node();
    node->key = *pre;
    if(len==1)
    {
        return node;
    }
    int i;
    for(i=0;i<len;++i)
    {
        if(inorder[i]==*pre)
        {
            break;
        }
    }
    node->left= CreateTree(pre+1,inorder,i);
    node->right = CreateTree(pre+i+1,inorder+i+1,len-i-1);
    return node;
}
int main()
{
    freopen("test.txt","r",stdin);
    int n;
    scanf("%d",&n);
    int pre[n],inorder[n];
    for(int i=0;i<n;++i)
    {
        scanf("%d",&pre[i]);
    }
    for(int i=0;i<n;++i)
    {
        scanf("%d",&inorder[i]);
    }
    Node* root = CreateTree(pre,inorder,n);
    post_travel(root);
    return 0;
}
View Code

     另一种解法:把树想象成一颗BST,根据中序遍历构造val与中序顺序自己的对应,然后遍历先序插入的时候,判断当前节点阶段编号是在val对应编号的左边还是右边(有没有很像bst的插入)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
using namespace std;

struct Node
{
    int val;
    Node* left;
    Node* right;
    Node(){left = right=NULL;}
};
map<int,int> valtoid;
Node* Insert(Node* node,int val)
{
    if(node==NULL)
    {
        node = new Node();
        node->val = val;
    }
    else if(valtoid[val]<valtoid[node->val])
        node->left = Insert(node->left,val);
    else
        node->right = Insert(node->right,val);
    return node;
}
void post_travel(Node* node)
{
    if(NULL==node) return;
    post_travel(node->left);
    post_travel(node->right);
    printf("%d ",node->val);
}
int main()
{
    int n;
    scanf("%d",&n); //输入个数
    int pre[n];
    for(int i=0;i<n;++i) scanf("%d",&pre[i]);   //输入先序
    for(int i=0;i<n;++i)    //输入后序
    {
        int a;
        scanf("%d",&a);
        valtoid[a] = i;
    }
    Node* root = NULL;
    for(int i=0;i<n;++i) root = Insert(root,pre[i]);
    post_travel(root);
    puts("");
}
/*测试
7
6 5 8 7 3 2 4
8 5 7 6 2 3 4
输出 8 7 5 2 4 3 6
*/
View Code

 

     2、已知先序和后序,求中序(PAT原题1119)

    难点在先序和后序构造的树不一定唯一,当然也有可能唯一解。所以我们要知道什么时候二叉树不唯一,什么时候唯一呢?那例题来说1 2 3 4 6 7 5和2 6 7 4 5 3 1,我们第一步可以得到当前父节点1。然后如何判断左右孩纸呢。

先序1后面那个就是下一个节点(2),后序1前面那个就是下一个节点(3),这时候你发现2和3不一样,说明什么呢--其实说明一个是左孩纸,一个是有孩纸。然后从先序中找右孩纸(3)的节点位置,然后我们可以找到左右孩纸的两颗树的序:左孩纸树的先序序列2,后序也是2,右孩纸的先序序列就是3 4 6 7 5,后序序列是6 7 4 5 3。然后不断重复以上步骤就好了。

再看另一个例子:1 2 3 4、2 4 3 1。为什么不唯一呢,找左右孩纸序列,左孩纸2 右孩纸3 4(先序),4 3(后序)。然后对右孩纸进行递归,你会发现根节点是3,孩纸节点只有一个(4),然后你就不知道他是属于左节点还是右节点了,所以不唯一。

所以结论:先序和后序求中序,要求必须是孩纸节点要么没有,要么是2个,不能有任意节点只有一个孩纸节点。

#include<iostream>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<iterator>
#include<algorithm>
#include<cstring>
using namespace std;
struct Node
{
    int key;
    Node* left;
    Node* right;
    Node()
    {
        left = right = NULL;
    }
};
bool bOnly = true;
Node* CreateTree(int* pre,int *post,int len)
{
    Node *node = new Node();
    node->key = *pre;
    if(len != 1)
    {
        //printf("len=%d\n",len);
        if(*(pre+1) == *(post+len-2))
        {
            bOnly = false;
            node->left = CreateTree(pre+1,post,len-1);
        }
        else
        {
            int rightkey = *(post+len-2);
            int rightindex = 0;
            for(;rightindex<len;++rightindex)
            {
                if(rightkey == *(pre+rightindex))
                {
                    break;
                }
            }
            node->left = CreateTree(pre+1,post,rightindex-1);
            node->right = CreateTree(pre+rightindex,post+rightindex-1,len-rightindex);
        }
    }
    return node;
}
bool bFirst = true;
void inorder_travel(Node* root)
{
    if(root == NULL) return;
    inorder_travel(root->left);
    if(bFirst) bFirst = false;
    else printf(" ");
    printf("%d",root->key);
    inorder_travel(root->right);
}
int main()
{
    //freopen("test.txt","r",stdin);
    int n;
    scanf("%d",&n);
    int pre[n],post[n];
    for(int i=0;i<n;++i)
        scanf("%d",&pre[i]);
    for(int i=0;i<n;++i)
        scanf("%d",&post[i]);
    Node* root = CreateTree(pre,post,n);
    if(bOnly)
    {
        printf("Yes\n");
    }
    else
    {
        printf("No\n");
    }
    inorder_travel(root);
    printf("\n");
    return 0;
}
View Code

 

        3、完全二叉树,已知中序排序求广度排序(PAT1064原题)

  1064题是用二叉搜索树,所以先需要排序一下,然后构造树就行了

#include<iostream>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<iterator>
#include<algorithm>
#include<cstring>
using namespace std;
struct Node
{
    int key;
    Node* left;
    Node* right;
    Node(){left=right=NULL;}
};
int n;
int index2 = 0;
Node* CreateTree(int* a,int k)
{
    if(k>=n) return NULL;
    Node* node = new Node();
    node->left = CreateTree(a,2*k+1);
    node->key = a[index2++];
    node->right = CreateTree(a,2*k+2);
    return node;
}
bool bFirst = true;
void level_travel(vector<Node*> vn)
{
    if(vn.size()==0) return;
    vector<Node*> vnnext;
    for(int i=0;i<vn.size();++i)
    {
        if(bFirst) bFirst = false;
        else printf(" ");
        printf("%d",vn[i]->key);
        if(vn[i]->left) vnnext.push_back(vn[i]->left);
        if(vn[i]->right) vnnext.push_back(vn[i]->right);
    }
    level_travel(vnnext);
}
int main()
{
    //freopen("test.txt","r",stdin);
    scanf("%d",&n);
    int inorder[n];
    for(int i=0;i<n;++i)
        scanf("%d",&inorder[i]);
    sort(inorder,inorder+n);
    Node* root = CreateTree(inorder,0);
    vector<Node*> vn;
    vn.push_back(root);
    level_travel(vn);
    return 0;
}
View Code

       另一种求法:

int index2 = 0;
void createtree(int* a,int* inorder,int k,int n)
{
    if(k>=n) return;
    createtree(a,inorder,2*k+1,n);
    a[k] = inorder[index2++];
    createtree(a,inorder,2*k+2,n);
}
//广度求中序
void createtree2(int* a,int* inorder,int k,int n)
{
    if(k>=n) return;
    createtree2(a,inorder,2*k+1,n);
    inorder[index2++] = a[k];
    createtree2(a,inorder,2*k+2,n);
}
View Code

 

   4、搜索二叉树,已知先序排序求后序排序(PAT1135题)

  由于是搜索二叉树,可以知道左节点肯定比父节点小,右节点肯定比父节点大,先序排序其实就是插入的顺序,根据插入的顺序求树。

#include<iostream>
#include<cstdio>
using namespace std;
struct Node
{
    int val;
    Node* left;
    Node* right;
    Node(){left=right=NULL;}
};
Node* Insert(Node* node,int val)
{
    if(node==NULL)
    {
        node = new Node();
        node->val = val;
        return node;
    }
    if(val < node->val)
        node->left = Insert(node->left,val);
    else if(val > node->val)
        node->right = Insert(node->right,val);
    return node;
}
void post_travel(Node* node)
{
    if(node==NULL) return;
    post_travel(node->left);
    post_travel(node->right);
    printf("%d ",node->val);
}
int main()
{
    int n;
    Node* root = NULL;
    scanf("%d",&n);
    for(int i=0;i<n;++i)
    {
        int a;
        scanf("%d",&a);
        root = Insert(root,a);
    }
    post_travel(root);
    printf("\n");
    return 0;
}
/*测试数据1
7
4 2 1 3 6 5 7
输出:1 3 2 5 7 6 4
*/
/*测试数据2
7
3 2 1 6 4 5 7
输出:1 2 5 4 7 6 3
*/
View Code

     

       5、关于后序和中序构造数与先序和中序构树的关系,并且如何用一个算法就能通用的构树。https://www.cnblogs.com/jlyg/p/10402919.html