1. 程式人生 > >關於森林的建立和相關操作(待完善)

關於森林的建立和相關操作(待完善)

森林的建立一般來說有三種結構:雙親,孩子連結串列,孩子兄弟,其中孩子連結串列是使用最廣泛的,雙親表示主要反映的是一種鄰接關係,孩子連結串列也是如此,因此,這兩種結構主要是應用在圖的儲存中,表示鄰接矩陣和鄰接表,而孩子兄弟表示法則是樹和森林的最佳儲存。

首先是對於他的實現:我在這裡介紹一種比較麻煩的方法,也就是通過建立雙親表示的森林來間接建立二叉連結串列。

  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 }