关于森林的建立和相关操作(待完善)

森林的建立一般来说有三种结构:双亲,孩子链表,孩子兄弟,其中孩子链表是使用最广泛的,双亲表示主要反映的是一种邻接关系,孩子链表也是如此,因此,这两种结构主要是应用在图的存储中,表示邻接矩阵和邻接表,而孩子兄弟表示法则是树和森林的最佳储存。

首先是对于他的实现:我在这里介绍一种比较麻烦的方法,也就是通过建立双亲表示的森林来间接建立二叉链表。

  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 }

猜你喜欢

转载自www.cnblogs.com/qianqianjunzi/p/10130389.html