Vijos1114解題報告(不建樹解決二叉樹問題)


描述:

我們可以把由“0”和“1”組成的字符串分爲三類:全“0”串稱爲B串,全“1”串稱爲I串,既含“0”又含“1”的串則稱爲F串。

FBI樹是一種二叉樹1,它的結點類型也包括F結點,B結點和I結點三種。由一個長度爲2^N的“01”串S可以構造出一棵FBI樹T,遞歸的構造方法如下:
1) T的根結點爲R,其類型與串S的類型相同;
2) 若串S的長度大於1,將串S從中間分開,分爲等長的左右子串S1和S2;由左子串S1構造R的左子樹T1,由右子串S2構造R的右子樹T2。

現在給定一個長度爲2^N的“01”串,請用上述構造方法構造出一棵FBI樹,並輸出它的後序遍歷2序列。

 

解題思路:使用函數求出每個節點,然後通過該節點構建樹,最後遍歷樹即可。

 

難點:建樹函數void buildtree(int start, int end, treeNode* &root) 當start與end相等時,如果遞歸參數不當,如構建左子樹使用函數buildtree(start, (start+end)/2, root->lc),會陷入無限遞歸的深淵。

處理方法:

1:求串的左半邊時用(start+end+1)/2-1;求右半邊用(start+end)/2+1, 構建函數最開始用判斷條件start>end時return;

2: 構建函數在遞歸調用前判斷start是否等於end如果等於不遞歸調用函數;


代碼:

#include <cstdio>
#include <cstring>
#include <cmath>

const int MAXN = 2000;
char str[MAXN];
int nodenum;
class treeNode
{
    public:
    treeNode *lc;
    treeNode *rc;
    char c;
};
char findRoot(int start, int en)
{
    if(start > en)
        return 'N';
    int i;
    char first = str[start];

    for(i=start; i <= en; i++)
    {
        if(str[i] != first)
            return 'F';
    }
    if(first == '0')
        return 'B';
    return 'I';

}
void buildtree(int start, int end, treeNode* &root)
{
    if(start > end)
    {
        root = NULL;
        return;
    }
    char rootc;
    rootc = findRoot(start, end);
    root = new treeNode;
    root->c = rootc;
    buildtree(start,(start + end + 1) / 2-1, root->lc);
    buildtree((start + end) / 2+1, end, root->rc);
}
void lvisit(treeNode *root)
{
    if(root == NULL) return;
    lvisit(root->lc);
    lvisit(root->rc);
    printf("%c", root->c);
}
int main()
{
    scanf("%d", &nodenum);
    scanf(" %s", str);
    nodenum = pow(2, nodenum);
    treeNode *root = NULL;
    buildtree(0, nodenum-1, root);
    lvisit(root);
    printf("\n");
}

優化:

上面的方法過於麻煩:

其實由於樹是先序建立的所以如果建立根結點後,遞歸建立左子樹和右子樹前就把節點的值輸出,那麼樹建立結束的同時就輸出樹的先序遍歷序列。如果在建立左右子樹後再輸出節點的值,那麼最後輸出了樹後序遍歷序列。如果在建立左子樹後,建立右子樹前輸出節點的值,那麼最後輸出了樹的後序遍歷序列。

只要符合先序建樹的條件就可以用這種方法。

這樣以來就不用保存樹的節點,也不用在遍歷樹了。減少了時間複雜度和空間複雜度。


代碼如下:

使用處理方法1:

#include <cstdio>
#include <cstring>
#include <cmath>
const int MAXN = 1050;
char str[MAXN];
int nodenum;
char findRoot(int start, int en)
{
    while(start<en)
    {
        if(str[start] != str[en])
            return 'F';
        start++;
    }
    if(str[en] == '0')
        return 'B';
    return 'I';
}
void buildtree(int start, int end)
{
    if(start > end)
        return;
    buildtree(start,(start + end + 1) / 2-1);
    buildtree((start + end) / 2+1, end);
    printf("%c", findRoot(start, end));
}
int main()
{
    scanf("%d", &nodenum);
    scanf(" %s", str);
    buildtree(0, pow(2, nodenum)-1);
    printf("\n");
}

進一步優化:

在求每個節點時,其實不用重新遍歷串了,只需根據該節點左右孩子就可以確定該節點是F還是B還是I。如果左右節點不同,該節點爲F,否則該節點和其左右節點相同。這樣時間複雜度會進一步降低,代碼如下:

#include <cstdio>
#include <cstring>
#include <cmath>
const int MAXN = 1025;
char str[MAXN];
int nodenum;
char buildtree(int start, int end)
{
    char a;
    if(start != end)
    {
        a = buildtree(start,(start + end)/2);
        if(a!= buildtree((start + end)/2+1, end)) a = 'F';
        printf("%c", a);
        return a;
    }
    if(str[start] == '0') a = 'B';
    else a = 'I';
    printf("%c", a);
    return a;
}
int main()
{
    scanf("%d", &nodenum);
    scanf(" %s", str);
    buildtree(0, pow(2, nodenum)-1);
    printf("\n");
}




發佈了31 篇原創文章 · 獲贊 12 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章