Chinaunix首页 | 论坛 | 博客
  • 博客访问: 157032
  • 博文数量: 31
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 360
  • 用 户 组: 普通用户
  • 注册时间: 2017-02-28 08:37
个人简介

没有绝活,怎能风骚.....

文章分类

全部博文(31)

文章存档

2017年(31)

我的朋友

分类: C/C++

2017-04-11 00:00:12

    上数据结构讲二叉树时,没有好好听,对知道二叉树的前序和中序求后序或知道中序和后序求前序等问题,可以说是一直不懂,只单单会背:前序(根左右)、中序(左根右)和后序(左右根),口诀是记住了,但是不会灵活运用。
    直到看了一篇博客,网址是:,看完后让我豁然开朗,终于搞懂了。顺便总结下二叉树前序、中序和后序遍历相互求法,即如果只知道两个遍历,如何求第三种遍历方法,比较笨的方法是画出二叉树,然后根据各种遍历不同的特性来求,也可以编程求出,编程求出后面有时间再补上,现在先讲解画二叉树求法。
    首先,我们得清除前序、中序和后序的遍历特性:
    前序遍历(根左右):
        ①访问根节点
        ②前序遍历左子树
        ③前序遍历右子树
    中序遍历(左根右):
        ①中序遍历左子树
        ②访问根节点
        ③中序遍历右子树
    后序遍历(左右根):
        ①后序遍历左子树
        ②后序遍历右子树
        ③访问根节点
        
一、已知前序、中序遍历,求后序遍历
    例:前序遍历:GDAFEMHZ   中序遍历:ADEFGHMZ
    画树求法:
        第1步:根据前序遍历的特点,我们可以知道根节点为G。
        第2步:根据中序遍历ADEFGHMZ,根据“左根右”遍历规则,其中根节点G左侧的ADEF必然是根的左子树,G右侧的HMZ必然是根的右子树。
        第3步:观察左子树ADEF,左子树中的根节点必然是大树根节点G的左孩子;在前序遍历中,大树的根节点G的左孩子位于根节点G之后,所以左子树的根节点为D。
        第4步:同理可得根节点G的右子树节点HMZ的根节点也可以通过前序遍历求得。在前序遍历中,一定是先把根节点G和根节点G的所有左子树节点遍历完之后才会遍历右子树,并且遍历的左子树的第一个节点就是左子树的根节点;同理,遍历的右子树的第一个节点就是右子树的根节点。
        第5步:观察发现,上面的过程是递归的。先根据前序找到当前树的根节点,然后根据中序划分左子树,右子树,然后进入左子树重复上面的过程,然后进入右子树重复上面的过程。最后就可以还原一棵树了。递归的过程可以简洁表达为:
        ①确定根,确定左子树,确定右子树
        ②在左子树中递归
        ③在右子树中递归
        ④打印根
    那么,可以画出这个二叉树的形状为:
        
    那么,根据后序的遍历规则“左右根”,可以知道,后序遍历顺序为:AEFDHZMG

    编程求法:(根据上面的思路,写递归程序)
    

点击(此处)折叠或打开

  1. #include <iostream>
  2. #include <string>

  3. using namespace std;

  4. struct node//二叉树结点
  5. {
  6.     struct node *left;//左孩子
  7.     struct node *right;//右孩子
  8.     char data;//当前结点的值
  9. };

  10. void binaryTreeOrdering(char *pr,char *in,int len)
  11. {
  12.     if(0 == len)//递归返回条件
  13.     {
  14.         return;
  15.     }
  16.     struct node *curNode = new struct node;//创建新结点
  17.     curNode->data = *pr;//保存当前前序遍历中的根结点
  18.     int rootIndex = 0;//当前根结点的下标
  19.     for(;rootIndex < len;rootIndex++)//遍历当前中序遍历,求出当前根结点的下标
  20.     {
  21.         if(in[rootIndex] == curNode->data)
  22.             break;
  23.     }
  24.     //递归查看当前根结点是否还有左孩子
  25.     binaryTreeOrdering(pr+1,in,rootIndex);
  26.     //递归查看当前根结点是否还有右孩子
  27.     binaryTreeOrdering(pr+rootIndex+1,in+rootIndex+1,len-(rootIndex+1));
  28.     cout<<curNode->data;//输出当前根结点
  29.     //注:其实就是“左右根”的编程思想
  30.     return;
  31. }

  32. int main(int argc, const char *argv[])
  33. {
  34.     char *pr = "GDAFEMHZ";//前序遍历
  35.     char *in = "ADEFGHMZ";//中序遍历
  36.     //递归输出后序遍历
  37.     binaryTreeOrdering(pr,in,8);
  38.     cout<<endl;
  39.     return 0;
  40. }

二、已知中序和后序遍历,求前序遍历
    依然是上面的题,这次给出中序和后序遍历:
    中序遍历:ADEFGHMZ    后序遍历:AEFDHZMG
    画树求法:
    第1步:根据后序遍历的特点,我们知道后序遍历最后一个节点即为根节点,即为根节点G。
    第2步:根据中序遍历ADEFGHMZ,根据“左根右”遍历规则,其中根节点G左侧的ADEF必然是根的左子树,G右侧的HMZ必然是根的右子树。
    第3步:观察中序左子树ADEF,中序左子树中的根节点必然是大树根节点G的左孩子;在后序遍历“左右根”规则中,最后为根节点,大树的根节点G的左孩子为后序左子树AEFD中的D节点,所以左子树的根节点为D。
    第4步:同理可得根节点G的右子树节点HMZ的根节点也可以通过后序遍历求得。在后序遍历中,一定是先把根节点G的所有左子树节点遍历完之后再遍历右子树,最后遍历根节点G,并且遍历的左子树的最后一个节点就是左子树的根节点;同理,遍历的右子树的最后一个节点就是右子树的根节点。
    第5步:观察发现,上面的过程是递归的。先根据前序找到当前树的根节点,然后根据中序划分左子树,右子树,然后进入左子树重复上面的过程,然后进入右子树重复上面的过程。最后就可以还原一棵树了
    那么前序遍历为:GDAFEMHZ。
    
    编程求法:(根据上面的思路,写递归程序)
    

点击(此处)折叠或打开

  1. #include <iostream>
  2. #include <string>

  3. using namespace std;

  4. struct node//二叉树结点
  5. {
  6.     struct node *left;//左孩子
  7.     struct node *right;//右孩子
  8.     char data;//当前结点的值
  9. };

  10. void binaryTreeOrdering(char *af,char *in,int len)
  11. {
  12.     if(0 == len)//递归返回条件
  13.     {
  14.         return;
  15.     }
  16.     struct node *curNode = new struct node;//创建新结点
  17.     curNode->data = *(af+len-1);//保存当前后序遍历中的根结点
  18.     cout<<curNode->data;//输出当前根结点
  19.     int rootIndex = 0;//当前根结点的下标
  20.     for(;rootIndex < len;rootIndex++)//遍历当前中序遍历,求出当前根结点的下标
  21.     {
  22.         if(in[rootIndex] == curNode->data)
  23.             break;
  24.     }
  25.     //递归查看当前根结点是否还有左孩子
  26.     binaryTreeOrdering(af,in,rootIndex);
  27.     //递归查看当前根结点是否还有右孩子
  28.     binaryTreeOrdering(af+rootIndex,in+rootIndex+1,len-(rootIndex+1));
  29.     //注:其实就是“根左右”的编程思想
  30.     return;
  31. }

  32. int main(int argc, const char *argv[])
  33. {
  34.     char *af = "AEFDHZMG";//后序遍历
  35.     char *in = "ADEFGHMZ";//中序遍历
  36.     //递归输出前序遍历
  37.     binaryTreeOrdering(af,in,8);
  38.     cout<<endl;
  39.     return 0;
  40. }

    总结:
    已知前序和中序求后序:前序遍历可以知道第一个节点为根节点,再根据中序遍历和已知根节点,划分左子树和右子树,根据前序遍历“根左右”规则可知左子树和右子树的第一个节点也为根节点,再根据新的根节点和中序遍历,再次划分左子树和右子树,以此类推(递归),最后还原成一棵树。
    已知中序和后序求前序:后序遍历可以知道最后一个节点为根节点,再根据中序遍历和已知根节点,划分左子树和右子树,根据后序遍历“左右根”规则可知左子树和右子树的最后一个节点为根节点,再根据新的根节点和中序遍历,再次划分左子树和右子树,以此类推(递归),最后还原成一棵树。

阅读(2297) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~