森林的建立一般来说有三种结构:双亲,孩子链表,孩子兄弟,其中孩子链表是使用最广泛的,双亲表示主要反映的是一种邻接关系,孩子链表也是如此,因此,这两种结构主要是应用在图的存储中,表示邻接矩阵和邻接表,而孩子兄弟表示法则是树和森林的最佳储存。
首先是对于他的实现:我在这里介绍一种比较麻烦的方法,也就是通过建立双亲表示的森林来间接建立二叉链表。
1 bool CreateTreeFromFile ( Ptree *PT,char fileName[] ) 2 { 3 FILE *pFile; 4 char str[1000]; //存放读出的一行字符串 5 char strTmp[10] = ""; 6 pFile = fopen(fileName,"r"); 7 if ( pFile == NULL ) 8 { 9 cout<<"错误,文件打开失败!\n"; 10 return false; 11 } 12 13 while ( fgets(str,1000,pFile ) != NULL ) 14 { 15 TrimLeft(str); 16 if ( str[0] == '\n' ) 17 { 18 continue; 19 } 20 strncpy(strTmp,str,2); 21 22 if ( strcmp( strTmp,"//" ) == 0 ) 23 { 24 continue; 25 } 26 else 27 { 28 break; 29 } 30 } 31 if ( strcmp( str,"Tree or Forest\n" ) != 0 ) 32 { 33 cout<<"打开文件错误!"<<endl; 34 fclose(pFile); 35 return false; 36 } 37 while ( fgets(str,1000,pFile ) != 0 ) 38 { 39 TrimLeft(str); 40 if ( str[0] == '\n' ) 41 { 42 continue; 43 } 44 strncpy(strTmp,str,2); 45 46 if ( strcmp( strTmp,"//" ) == 0 ) 47 { 48 continue; 49 } 50 else 51 { 52 break; 53 } 54 } 55 //将各个节点的值放入数组中 56 char *token = strtok(str," "); 57 int nNum = 0; 58 while ( token != NULL ) 59 { 60 PT->node[nNum].data = *token; 61 PT->node[nNum].parent = -1; //这个很重要,奠定了森林的基础 62 token = strtok(NULL," "); 63 nNum++; 64 PT->n++; 65 } 66 //为数组中各个节点的parent指针赋值 67 68 int Np; //储存父节点下标 69 int Nc; //储存子节点下标 70 elementType datap; //储存父节点的值 71 elementType datac; //储存子节点的值 72 //循环取文件中的父子节点信息 73 while ( fgets(str,1000,pFile ) != NULL ) 74 { 75 TrimLeft(str); 76 if ( str[0] == '\n' ) 77 { 78 continue; 79 } 80 strncpy(strTmp,str,2); 81 82 if ( strcmp( strTmp,"//" ) == 0 ) 83 { 84 continue; 85 } 86 char *token = strtok(str," "); 87 datap = *token; 88 token = strtok(NULL," "); 89 datac = *token; 90 91 int j = 0; 92 while ( j < PT->n ) 93 { 94 if ( datap == PT->node[j].data ) 95 { 96 Np = j; 97 break; 98 } 99 j++; 100 } 101 j = 0; 102 while ( j < PT->n ) 103 { 104 if ( datac == PT->node[j].data ) 105 { 106 Nc = j; 107 break; 108 } 109 j++; 110 } 111 PT->node[Nc].parent = Np; 112 } 113 fclose(pFile); 114 } 115 bool CreateFNtree( Ptree *PT,FNnode *&FT,int w ) 116 { 117 int v1,v2; 118 FT = new FNnode; 119 FT->data = PT->node[w].data; 120 FT->firstson = NULL; 121 FT->nextbrother = NULL; 122 v1 = firstChild(PT,w); 123 if ( v1 != -1 ) 124 { 125 CreateFNtree(PT,FT->firstson,v1); 126 } 127 v2 = nextSibling(PT,w); 128 if ( v2 != -1 ) 129 { 130 CreateFNtree(PT,FT->nextbrother,v2); 131 } 132 }
当然,为了实现这钟麻烦的操作,我们还自定了许许多多的函数,这里就不再一一列举了,但是呢,博主是为了完成作业才这么写的,个人建议呢自己交互式输入。
下面就是针对二叉链表储存的树和森林的一些相关操作:
先序和后序遍历
1 void PreOrder ( FNnode *FT ) 2 { 3 if (FT) 4 { 5 cout<<FT->data<<","; 6 PreOrder(FT->firstson); 7 PreOrder(FT->nextbrother); 8 } 9 } 10 void PostOrder( FNnode *FT ) 11 { 12 if (FT) 13 { 14 PostOrder(FT->firstson); 15 cout<<FT->data<<","; 16 PostOrder(FT->nextbrother); 17 } 18 }
层次遍历
一般来说层次遍历都是需要队列参与的,这个也不例外:
1 void LevelOrder ( FNnode *FT ) 2 { 3 seqQueue Q; 4 initialQueue(&Q); 5 FNnode *temp = FT->nextbrother; 6 inQueue(&Q,FT); 7 while ( !queueEmpty(Q) || temp != NULL ) 8 { 9 while( temp != NULL ) 10 { 11 inQueue(&Q,temp); 12 temp = temp->nextbrother; 13 } 14 queueFront(Q,&temp); 15 cout<<temp->data<<","; 16 outQueue(&Q); 17 if ( temp->firstson != NULL ) 18 { 19 temp = temp->firstson; 20 } 21 else 22 { 23 temp = NULL; 24 } 25 } 26 }
我们通过简单的分析便可以发现,在原森林中同一层次的节点,在二叉链表的实现中都变成了他的nextbrother,而当前节点的firstson便变成了他的下一层次的节点的第一个,因此我们就会发现,我们只要每次将当前节点的所有的nextbrother全部入队,然后出队,指针移向firstson然后继续将nextbrother全部入队,依次类推,便可以简单的实现。
求森林的高度
1 int ForestHeight ( FNnode *FT ) 2 { 3 int h1; 4 int h2; 5 if ( FT == NULL ) 6 { 7 return 0; 8 } 9 if ( FT->firstson == NULL ) 10 { 11 return 1; 12 } 13 h1 = ForestHeight(FT->firstson) + 1; 14 h2 = ForestHeight(FT->nextbrother); 15 16 if ( h1 > h2 ) 17 { 18 return h1; 19 } 20 else 21 { 22 return h2; 23 } 24 25 }
这个求森林的高度的函数是用递归来实现的,其实他的实现过程与二叉树的实现比较类似,只是要注意,在实现时nextbrother指向的是和他同一层次的节点而firstson指向的是下一层节点,最后要注意出口。
下面是求森林节点数和叶子节点数的函数,森林节点的数目不再多说,叶子结点要注意,在森林中是叶子结点的不一定在二叉链表中是叶子结点,而在二叉链表中是叶子结点在森林中一定是叶子结点,其中的区别在于,在森林转化为二叉链表的过程中,一些叶子结点有兄弟节点,因此就变成了连着兄弟节点的分支,但是注意,它是叶子节点,就保证了它是没有左子树的,而在二叉链表中,左子树也就是firstson就表示着有子节点,因此,只要是左子树为空,那么他就是一个叶子节点。
1 int ForestNode ( FNnode *FT ) 2 { 3 if ( FT == NULL ) 4 { 5 return 0; 6 } 7 else 8 { 9 return ForestNode(FT->firstson) + ForestNode(FT->nextbrother) + 1; 10 } 11 } 12 int LeafNode ( FNnode *FT ) 13 { 14 if ( FT == NULL ) 15 { 16 return 0; 17 } 18 if ( FT->firstson == NULL ) 19 { 20 return 1 + LeafNode(FT->nextbrother); 21 } 22 return LeafNode(FT->firstson) + LeafNode(FT->nextbrother);
求森林的度数:
这里森林的度数,其实就是求每个节点的度数,然后进行比较,而每个节点的度数,其实就是子节点的个数,而子节点的个数就是firstson的brother的个数,所以代码显而易见
1 int NodeDegree ( FNnode *FT ) 2 { 3 FNnode *temp = FT->firstson; 4 int i = 0; 5 6 while ( temp != NULL ) 7 { 8 temp = temp->nextbrother; 9 i++; 10 } 11 return i; 12 } 13 int ForestDegree ( FNnode *FT,int *h ) 14 { 15 if (FT) 16 { 17 int temp; 18 temp = NodeDegree(FT); 19 if ( temp > *h ) 20 { 21 *h = temp; 22 } 23 ForestDegree(FT->firstson,h); 24 ForestDegree(FT->nextbrother,h); 25 } 26 }
求节点的层次
这里求节点的层次,我借鉴了二叉树的经验,分步来求,传入H表示层次数,先在firstson中寻找,递归的过程H+1,然后再在nextbrother中寻找,递归过程不+1,在找到时,就返回当前的h
1 int NodeLevel ( FNnode *FT,elementType x ,int h ) 2 { 3 int l; 4 if ( FT == NULL ) 5 { 6 return 0; 7 } 8 if ( FT->data == x ) 9 { 10 return h; 11 } 12 else 13 { 14 l = NodeLevel(FT->firstson,x,h + 1); 15 if ( l != 0) 16 { 17 return l; 18 } 19 else 20 { 21 l = NodeLevel(FT->nextbrother,x,h); 22 } 23 } 24 }
广义表输出:
我们可以简单的发现,广义表的输出顺序是和先序输出相同的,因此,我们在先序输出的基础上,对括号进行控制,便可以很简单的实现算法
1 void Lists ( FNnode *FT ) 2 { 3 if (FT) 4 { 5 cout<<FT->data; 6 if ( FT->firstson != NULL ) 7 { 8 cout<<"("; 9 Lists(FT->firstson); 10 } 11 if ( FT->nextbrother != NULL ) 12 { 13 Lists(FT->nextbrother); 14 } 15 else 16 { 17 cout<<")"; 18 } 19 } 20 }