zoukankan      html  css  js  c++  java
  • 【转】ACM 2567 -- 树的Prufer编码

    本文介绍北京大学ACM网站2567号题目的解法。介绍部分基本翻译自网站上的题目介绍。

    题目介绍:
       给定一棵各节点编号为整数1,2,3...n的树(例如,无环连通图),其Prufer编码(Prufer code,不知道有没有标准的译法,用金山词霸没有查到,用Google也没有搜索到)构造方法如下:从树中去掉编号值最小的叶子节点(仅与一条边邻接的节点),以及与它邻接的边后,记下与它邻接的节点的编号。在树中重复这个过程,知道只剩下一个节点(总是编号为n的节点)为止。记下的n-1个编号序列就是树的Prufer编码。你的任务是计算给定的树的Prufer编码。树用下列语法表示:
      T-->(N S)
      S--> T S | empty
      N-->number
    就是说,树由一对括号包围一个表示根节点的数,以及其后跟随的任意多个(可能没有)由单个空格分隔的子树组成。(这里的表示方法类似编译原理中的产生式)根据这个定义,树的根节点也可能是一个叶子节点。
    输入:
       由多个测试案例组成。每个案例用输入文件的一行以上述格式表示一棵树。EOF表示输入结束。可以假定1 <= n <= 50。
    输出:
       对于每个测试案例产生包含给定树的Prufer编码的单行输出。数之间由单个空格分隔。不得在行末尾添加任何空格。
    示例输入:
    (2 (6 (7)) (3) (5 (1) (4)) (8))
    (1 (2 (3)))
    (6 (1 (4)) (2 (3) (5)))
    示例输出:
    5 2 5 2 6 2 8
    2 3
    2 1 6 2 6
     
    程序:
    1   
    2    #include
    3    #include
    4    #include
    5   
    6    #define  END        0  // 结束
    7    #define  L_BRACKET  1  // 左括号
    8    #define  R_BRACKET  2  // 右括号
    9    #define  NUMBER     4  // 数
    10  
    11   typedef struct _node
    12   {
    13      int value;  // 节点值
    14      int childs; // 子节点数
    15      struct _node *parent;      // 父节点
    16      struct _node *first_child; // 第一个孩子节点
    17      struct _node *sibling;     // 下一个兄弟节点
    18   }NODE;
    19  
    20   typedef struct _token
    21   {
    22      int type;  // 标记类型(左、右括号,数)
    23      int value; // 值(仅当类型为数时有效)
    24   }TOKEN;
    25  
    26   typedef void (*FUNC)(NODE *);
    27   static NODE *pSmallest = 0;// 指向最小的叶子节点
    28  
    29  
    34   char *GetNextToken(char *input,TOKEN *pToken)
    35   {
    36      char *p,ch;
    37      int value;
    38  
    39      pToken->type = END;
    40     
    41      p = input;
    42      while ((ch = *p) && ch != '(' && ch != ')' && !isdigit(ch))
    43          p++;
    44      switch(ch)
    45      {
    46      case '(': pToken->type = L_BRACKET; p++; break;
    47      case ')': pToken->type = R_BRACKET; p++; break;
    48      default:
    49         
    50          if (isdigit(ch))
    51          {
    52              value = 0;
    53              while ((ch = *p) && isdigit(ch))
    54              {
    55                  value = 10 * value + (ch - '0');
    56                  p++;
    57              }
    58              pToken->type  = NUMBER;
    59              pToken->value = value;
    60          }
    61          break;
    62      }
    63     
    64      return (*p == '' ? 0 : p);
    65   }
    66  
    67  
    71   NODE *BuildTree(char *input)
    72   {
    73      char *p;
    74      TOKEN token;
    75      NODE  *pCur,*pNew,*pRoot,*pLast;
    76  
    77      p = input; pCur = pLast = pRoot = 0;
    78      do
    79      {
    80          p = GetNextToken(p,&token);
    81          switch(token.type)
    82          {
    83          case L_BRACKET:
    84              break;
    85          case R_BRACKET:
    86             
    87              pLast = pCur;
    88              if (0 != pCur)
    89                  pCur = pCur->parent;
    90              break;
    91          case NUMBER:
    92              pNew = (NODE *)malloc(sizeof(NODE));
    93              memset(pNew,0,sizeof(NODE));
    94              pNew->value = token.value;
    95             
    96              pNew->parent = pCur;
    97              if (0 != pLast)
    98              {
    99                  pLast->sibling = pNew;
    100                 pLast = pNew;
    101             }
    102             if (0 != pCur)
    103             {
    104                 if (0 == pCur->childs++)
    105                     pCur->first_child = pNew;
    106             }
    107             else pRoot = pNew;
    108 
    109             p = GetNextToken(p,&token);
    110             if (token.type == L_BRACKET)
    111             {
    112                 pCur = pNew;
    113                 pLast = 0;
    114             }
    115             else if (token.type == R_BRACKET)
    116                 pLast = pNew;
    117             break;
    118         }
    119     }while (0 != p);
    120     return pRoot;
    121  }
    122 
    123 
    127  void TravelTree(NODE *pRoot,FUNC func)
    128  {
    129     NODE *pCur;
    130     if (0 == pRoot) return;
    131     (*func)(pRoot);
    132     pCur = pRoot->first_child;
    133     while (pCur)
    134     {
    135         TravelTree(pCur,func);
    136         pCur = pCur->sibling;
    137     }
    138  }
    139 
    140 
    141  int IsLeafNode(NODE *pNode)
    142  {
    143     return (0 != pNode && (0 == pNode->childs && 0 != pNode->parent)
    144     || (1 == pNode->childs && 0 == pNode->parent));
    145  }
    146 
    147 
    148  void LeafValueCompare(NODE *pNode)
    149  {
    150     if (IsLeafNode(pNode))
    151         if (0 == pSmallest) pSmallest = pNode;
    152         else if (pNode->value < pSmallest->value)
    153             pSmallest = pNode;
    154  }
    155 
    156 
    161  NODE *ReleaseLeafNode(NODE *pRoot,NODE *pNode)
    162  {
    163     NODE *pParent,*pSibling;
    164     if (0 == pNode->childs)
    165     {
    166         pParent = pNode->parent;
    167         if (pParent)
    168         {
    169             pSibling = pParent->first_child;
    170             if (pNode == pSibling)
    171                 pParent->first_child = pNode->sibling;
    172             else
    173             {
    174                 while (pSibling && pSibling->sibling != pNode)
    175                     pSibling = pSibling->sibling;
    176                 if (pSibling)
    177                     pSibling->sibling = pNode->sibling;
    178             }
    179             pParent->childs--;
    180         }
    181         free(pNode);
    182     }
    183     else
    184     {
    185         pRoot = pNode->first_child;
    186         pRoot->parent = 0;
    187         free(pNode);
    188     }
    189     return pRoot;
    190  }
    191 
    192  void ReleaseTree(NODE *pRoot)
    193  {
    194     NODE *pCur,*pNext;
    195     if (pRoot)
    196     {
    197         pCur = pRoot->first_child;
    198         while (pCur)
    199         {
    200             pNext = pCur->sibling;
    201             ReleaseTree(pCur);
    202             pCur = pNext;
    203         }
    204         free(pRoot);
    205     }
    206  }
    207 
    208  int main(int argc,char *argv[])
    209  {
    210     NODE *pRoot;
    211     char line[250];
    212     int  n;
    213     while (!feof(stdin))
    214     {
    215         line[0] = '';
    216         gets(line);
    217         pRoot = BuildTree(line);
    218         n = 0;
    219         while (pRoot && pRoot->childs != 0)
    220         {
    221             pSmallest = 0;
    222             TravelTree(pRoot,LeafValueCompare); // 遍历树
    223             if (0 == pSmallest->childs)
    224                 printf(n++ ? " %d" : "%d",pSmallest->parent->value);
    225             else
    226                 printf(n++ ? " %d" : "%d",pSmallest->first_child->value);
    227             pRoot = ReleaseLeafNode(pRoot,pSmallest);
    228         }
    229         if (0 != pRoot)
    230         {
    231             printf(" ");
    232             ReleaseTree(pRoot);
    233         }
    234     }
    235     return 0;
    236  }
    237 
     
    说明:
    1 解决此问题的第一个难点就在于如何利用输入的一行数据建立树。程序中BuildTree()函数用于建立树。函数中pCur表示当前处理的树层次父节点,当前层的节点都是它的孩子节点;pLast表示最近处理的一个节点,新建的节点将成为它的兄弟节点,放在其右边;pRoot当然就表示根节点了。BuildTree()读入一行中的各种Token进行处理,直到一行处理完成。
    (1)第85至90行:遇到右括号时表示某子树结束,返回到上一层。因为子树也是树,而根据上面的定义T-->(N S)知道,右括号是树的结束符(编译原理里面有专门的术语的,不记得是不是“结束符”)。遇到右括号就表示某子树处理完成了,需要返回到上一级,处理这个子树的下一个右兄弟节点(可能是某子树的根)
    (2)第92到94行:遇到一个数时,建立一个它所表示的新的树节点。
    (3)第95到107行:设定父兄节点。直接看这段代码还是不太好理解的,看下面这张示意图就好理解了:
    ACM <wbr>2567 <wbr>-- <wbr>树的Prufer编码
    (4)第109到116行:根据下一个Token类型决定做什么动作,这个好像也跟编译原理里的某个理论有关系,也记不清楚了。这里我看代码115行时候有点疑惑了:如果下一个符号既不是左括号,又不是右括号呢?仔细想想,根据上面的语法定义,数后面是不会跟随一个数的(在编译原理上好像所谓的“尾随符号”就是表示这个的,在书上叫做follow符号集)
     
    2 树建立好之后的任务就是生成Prufer编码。我采用的方法比较笨:不停地遍历树,每次遍历找到编号最小的叶子节点,输出它的父节点的编号,将它从树中去掉,直到树只剩下一个节点。
    (1)127到138行:对树进行前序遍历,采用的是递归的方法。不过在遍历的之前先调用148行的LeafValueCompare()函数,来记录编号最小的叶子节点。
    (2)141到145行:IsLeafNode()函数判断节点是不是叶子节点。注意最后一个条件:(1 == pNode->childs && 0 == pNode->parent)。这就是上文说的,树的根节点也可能是叶子节点。也就是树根节点在没有任何孩子节点时就也是叶子节点。
       关于生成Prufer编码,我想应该有效率更高的方法:只进行一次遍历,记下各个节点的编号值以及节点指针,按照节点编号从小到大进行排序;以后从小到大进行搜索,搜索到一个叶子节点就把它去掉,记下其父节点编号。直到树只剩下一个节点。
  • 相关阅读:
    TCP协议-如何保证传输可靠性
    计算机网络基础(未完待续)
    计算机理论基础
    计算机网络
    操作系统与计算机网络
    Linux系统中的vi/vim指令【详解】
    Linux面试笔试题带答案【详解】
    关于梦想(五)
    Jmeter的安装教程【图文】
    关于梦想(四)
  • 原文地址:https://www.cnblogs.com/flyptt/p/3419411.html
Copyright © 2011-2022 走看看