[NOIP2001 普及组] 求先序排列题解

这里附上题目链接:[NOIP2001 普及组] 求先序排列题解。

										~~手动分割~~ 

题目

[NOIP2001 普及组] 求先序排列题解_第1张图片[NOIP2001 普及组] 求先序排列题解_第2张图片

思路解析

我将整棵二叉树的根称为根结点,子树的根称为父结点,以便于区分。
[NOIP2001 普及组] 求先序排列题解_第3张图片

1.重构二叉树+先序遍历

因为题目给出了二叉树的中序排列和后序排列,根据中序排列+后序排列唯一确定一棵二叉树的定理,可

  1. 重构此棵二叉树。
  2. 然后对二叉树进行先序遍历输出它的先序排列。

对于不知道如何重构二叉树的读者,可以参考我的另一篇文章二叉树的重构与遍历。

AC代码

#include 
#include 
#include 

struct node//node表示树的结点
{
    char data;//data意为数据域
    struct node *left_child;//left_child意为左子树的父结点
    struct node *right_child;//right_child意为右子树的父结点
};

//postorder为当前子树的后序排列末尾元素的地址,inorder为当前子树的中序排列首元素的地址
//length为当前子树的后序排列的长度,parent_node为当前要插入的结点的双亲结点的地址
struct node *reconstruct_binary_tree2(char *postorder,char *inorder,int length,struct node *parent_node,int n)
{
    int left_length=0,right_length=0;//left_length为当前根结点左子树的先序排列的长度,right_length为当前根结点左子树的先序排列的长度
    struct node *cur_inser_node=(struct node *)malloc(sizeof(struct node));//先给要插入二叉树的结点分配内存空间
    char *root_node=postorder;//当前子树的前序排列的后序排列的末尾元素即为当前子树的根结点
    char *border=inorder;//border为中间变量指针,接下来会用它来记录当前子树的根结点在中序排列中的位置,从而得到当前子树的左子树和右子树
    (cur_inser_node)->data=*postorder;//将父结点所代表的元素存入结构体的数据域
    (cur_inser_node)->left_child=NULL;
    (cur_inser_node)->right_child=NULL;
    if(n==0)//n=0说明此时插入的结点是二叉树的树根(也就是整棵二叉树的根结点),它是没有双亲结点的
    {
        parent_node=cur_inser_node;//之所以要进行这一步赋值是防止在下一个if判断中出现对值为0的指针的访问
    }
    if(parent_node->left_child==NULL||parent_node->right_child==NULL)//若当前要插入的结点的双亲结点的地址为空
    {
        if(n==1)//n=1说明正在重构parent_node的左子树
        {
            parent_node->left_child=cur_inser_node;//让双亲结点向左指向当前要插入的结点
        }
        else if(n==2)//n=2说明正在重构parent_node的右子树
        {
            parent_node->right_child=cur_inser_node;//让双亲结点向右指向当前要插入的结点
        }
    }
    if(length==1)//若前子树的先序排列的长度为1,说明当前要插入的结点没有左右子树,此时不需要再继续重构
    {
        return cur_inser_node;
    }
    while(*border!=*root_node)//寻找当前子树的父结点在中序排列中的位置
    {
        border++;
    }
    left_length=(int)(border-inorder);//left_length为当前结点的左子树的先序排列的长度
    right_length=(int)(length-left_length-1);//左子树和右子树先序排列的长度之和为length-1,因为在此之前已经往二叉树中插入了一个结点
    if(left_length>0)//重构左子树
    {
        reconstruct_binary_tree2(postorder-1-right_length,inorder,left_length,cur_inser_node,1);
    }
    if(right_length>0)//重构右子树
    {
        reconstruct_binary_tree2(postorder-1,inorder+left_length+1,right_length,cur_inser_node,2);
    }
    return cur_inser_node;
}

//先序遍历
int preorder_traversal(struct node *root_node)
{
    if(root_node==NULL)
    {
        return 0;
    }
    printf("%c",root_node->data);
    preorder_traversal(root_node->left_child);
    preorder_traversal(root_node->right_child);
    return 0;
}

int main()
{
    int length;
    struct node* root_node=NULL;
    char inorder_sequence[20],postorder_sequence[20],b;
    //输入数据
    scanf("%s%c",inorder_sequence,&b);
    scanf("%s%c",postorder_sequence,&b);
    length=strlen(inorder_sequence);
    //重构二叉树
    root_node=reconstruct_binary_tree2(postorder_sequence+length-1,inorder_sequence,length,root_node,0);
    //先序遍历
    preorder_traversal(root_node);
    return 0;
}

[NOIP2001 普及组] 求先序排列题解_第4张图片

2.不重构二叉树,直接输出前序排列

在思路1中,我用二叉链表作存储结构重构二叉树,在通过对其进行先序遍历输出先序排列。但重构这一步其实是多余的。或者说,如果要通过递归来对二叉树进行先序遍历的话,就必须要重构二叉树(使用数组二叉链表来存储二叉树的结构)。

但是我们知道,先序遍历的遍历顺序是根结点、左子树、右子树,而子树的后序排列的末尾元素是该子树的父结点,在知道了父结点元素后,可以在中序排列中分离出该父结点的左子树和右子树。知道了左子树和右子树的两个排列后,又可以分离出左子树和右子树的根(注意:结点的子树的根为该结点的孩子结点),不断递归地执行下去,直到分离出所有的结点。[NOIP2001 普及组] 求先序排列题解_第5张图片

所以对于此题,更优化的思路是不重构二叉树(即不使用任何数据结构存储二叉树的结构),通过题目输入的两个排列,不断地分离出二叉树的结点、结点的左子树以及父结点的右子树,直到分离出所有的结点。

#include 
#include 
#include 


struct node//node表示树的结点
{
    char data;//data意为数据域
    struct node *left_child;//left_child意为左子树的父结点
    struct node *right_child;//right_child意为右子树的父结点
};

//inorder为当前子树的中序排列首元素的地址,postorder为当前子树后序排列末尾元素的地址,length为当前子树的排列长度
int preorder_traversal(char *inorder,char *postorder,int length)
{
    char *border=inorder,*t=inorder;
    int left_length=0,right_length=0;
    printf("%c",*postorder);//输出父结点
    if(length==1)//若当前父结点没有左右子树
    {
        return 0;
    }
    while(*border!=*postorder)
    {
        border++;
    }
    left_length=(int)(border-t);//left_length为当前父结点的左子树的排列长度
    right_length=length-1-left_length;//right_length为当前结点的右子树的排列长度
    if(left_length>0)
    {
        preorder_traversal(inorder,postorder-1-right_length,left_length);//遍历左子树
    }
    if(right_length>0)
    {
        preorder_traversal(inorder+1+left_length,postorder-1,right_length);//遍历右子树
    }
    return 0;
}

int main()
{   //preorder为先序序列,inorder为中序序列,postorder为后序序列
    int length;
    char inorder_sequence[20],postorder_sequence[20],b;
    //输入数据
    scanf("%s%c",inorder_sequence,&b);
    scanf("%s%c",postorder_sequence,&b);
    length=strlen(inorder_sequence);
    //前序遍历
    preorder_traversal(inorder_sequence,postorder_sequence+length-1,length);
    return 0;
}

[NOIP2001 普及组] 求先序排列题解_第6张图片

你可能感兴趣的:(洛谷题解,二叉树,链表,数据结构,算法,指针)