用樹的遍歷求解層次問題

樹的性質:
(1)有且僅有一個節點沒有前件(父節點),該節點稱爲樹的根。
(2)除根外,每個節點都有且僅有一個前件。
(3)除根外,每個節點都有唯一的路徑連到根上。

層次型問題一般具備的結構特徵:
有且僅有一個初始狀態,所有相關因素按照不同屬性自上而下分解成若干層次。
樹的遍歷:
按照一定規律不重複的訪問訪問樹中的每一個節點。
遍歷過程實質上是將樹這種非線性結構轉化成線性結構。

1.先序遍歷樹:由上而下,由左而右。
規則:若樹爲空則退出;否則先序訪問樹的根節點。然後先序遍歷每棵子樹。

//僞代碼
void preorder(int v){
    for(i屬於v相鄰的節點集){//先序遍歷每個與v相鄰的未訪問節點
        if(節點i未被訪問){
            preorder(i);
        }
    }
}

由於先序遍歷中對任一節點的處理是在對他所有的子節點被處理之前進行的。常用於計算樹中節點的層次,節點至根的路徑等運算。

2.後續遍歷:由下而上,由左而右
規則:若樹爲空則退出。否則先一次後序遍歷每棵子樹,最後訪問根節點。

//僞代碼
void postorder(int v){
    for(i屬於v相鄰的節點集){
        if(節點i未被訪問){
            postorder(i);
        }
    }
    訪問處理節點v;
}

由於在後序遍歷中,任一節點處的工作是在他所有的子節點被處理之後進行的。適宜於統計相連的下層節點的狀態。如計算節點高度。子樹的節點總數。節點權和等等。

三種常用的樹的存儲:

(1)廣義表表示
樹中的節點可以分爲三種,葉節點,根節點,除根節點以外的其他非葉節點(也稱分支節點)。在廣義表中有三種節點與之對應。即原子節點,表頭節點,子表節點。
廣義錶鏈表:
假設一棵樹的括號表示法爲A(B(E,F),C(G(K,L)),D(H,I,J(M)))。
樹根節點A有三個非葉節點的子女,則在他的廣義錶鏈表中,表頭節點爲A,他有三個子表節點,各有一個廣義表子鏈表:第一個廣義表子鏈表的表頭節點B,他有兩個原子節點EF,以此類推。
(2)雙親表示
對樹進行後序遍歷時,一般採用雙親表示的存儲方式。以一組連續的存儲單元來存放樹的節點,每個節點有兩個域。一個是數據域data,用來存放數據元素。一個是父指針域,用來存放指示其雙親節點位置的指針。
(3)多重鏈表
對樹進行先序遍歷的時候,一般採用多重鏈表的存儲方式。即存儲每個節點的數據與子指針。一棵樹中每個節點的子數個數可能不相同。採用變長節點方式將給存儲管理帶來很多麻煩。我們可以根據樹的度d爲每個節點存儲d個指針域。壞處是管理空間很大很浪費。假設樹中有n個節點,就有n*d個指針域。實際上只有n-1個指針域被用到。也可以用STL中的vector進行存儲。

現在回想起來這道題。有沒有想到就是一個樹的先序遍歷+回溯。用vector存儲每個點的子節點。當時怎麼就。。。反應不過來呢。。數據結構學太渣。。。
http://blog.csdn.net/quanzw0120/article/details/72582135

Poj 1330 編寫一個程序計算樹中兩個不同節點的最近公共祖先。
輸入:
輸入由T個測試數據組成。
第一行給出測試用例數T。
每個測試數據第一行給出n(樹的節點數)。節點用(1~n)標示。
後面的n-1行,每行給出兩個整數;表示一條邊。第一個整數是第二個整數的父母節點。

https://vjudge.net/problem/POJ-1330

思路:
一開始看確實很慌。就是感覺理論知識掌握得很好。看別人的也看得很懂然後自己寫寫就感覺不會。
先想想一下。題目已經說了是一棵樹。那我們要選擇什麼樣的方式建樹?我們要記錄什麼東西?因爲要找共同的祖先。那一定要往回找。那我們需要記錄的是不是就是它的父節點?於是開了father數組記錄父節點。想要計算出層數就需要先序遍歷。於是找到了合適的存儲方式多重鏈表?於是開了一個vector記錄他的子節點。
建樹大概就是這樣。那如何去找它們共同祖先呢?假設我當前給出的這個節點在第6層,另一個節點在第9層。那是不是我要把在第九層的往回找。一直找到第6層,。這時候同層有可能就找到共同的祖先了,也有可能找不到,假設沒有找到就輪着往上找唄。

每次用STL庫的時候都要翻書。。有空總結一下STL庫。
至少vector不要再忘了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn=10005;
vector<int>vec[maxn];
int father[maxn];
int anstemp[maxn];
void dfs(int rec,int step){
    anstemp[rec]=step;
    vector<int>::iterator iter;
    for(iter=vec[rec].begin();iter!=vec[rec].end();iter++){
        dfs(*iter,step+1);
    }
}
int main(){
    //freopen("1.txt","r",stdin);
    int T,n,tempa,tempb,root,step,quesa,quesb,a,b;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            vec[i].clear();
        }//vector的初始化啊。別忘了。
        memset(father,-1,sizeof(father));
        memset(anstemp,0,sizeof(anstemp));
        for(int i=0;i<n-1;i++){//記錄父節點+子節點
            scanf("%d%d",&tempa,&tempb);
            tempa--;//這些++--其實都可以不要的,只是因爲喜歡從0開始,所以把所有的下標都--了。
            tempb--;
            vec[tempa].push_back(tempb);
            father[tempb]=tempa;
        }
        for(int i=0;i<n;i++){
            if(father[i]==-1){
                root=i;
            }
        }
        dfs(root,0);
        scanf("%d%d",&quesa,&quesb);
        quesa--;
        quesb--;
        while(quesa!=quesb){//其實我覺得這一段很妙的。就是不停反覆的往上找,也沒什麼好妙的。不這麼寫,,,那還能怎麼寫。
            if(anstemp[quesa]>anstemp[quesb]){
                quesa=father[quesa];
            }
            else{
                quesb=father[quesb];
            }
        }
        printf("%d\n",quesa+1);
    }
    return 0;
}

https://vjudge.net/problem/POJ-2003
就是一個關於公司關係層次的問題。鏈表用的不好。照着書敲的。回頭線性結構表要補補。還有一個bug就是c++過不了編譯。。交G++就過了。。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <list>
using namespace std;
struct Tman{
    string name;
    Tman *f;
    list<Tman *>s;
    Tman(){
        f=NULL;
    }
};
map<string,Tman*>has;
Tman* root;
void print(long dep,Tman *now){
    if(now==NULL) return;
    for(int i=0;i<dep;i++) cout<<"+";
    cout<<now->name<<endl;
    for(list<Tman*>::iterator j=now->s.begin();j!=now->s.end();j++){
        print(dep+1,*j);
    }
}
void hires(string s1,string s2){
    Tman *f=has[s1];
    Tman *s=new Tman();
    s->name=s2;
    s->f=f;
    f->s.push_back(s);
    has[s2]=s;
}
void fire(string n1){
    Tman *s=has[n1];
    Tman *f=s->f;
    has.erase(n1);
    while(s->s.size()!=0){
        s->name=s->s.front()->name;
        has[s->name]=s;
        s=s->s.front();
    }
    s->f->s.remove(s);
    delete s;
}
void solve(){
    string s1,s2;
    long i;
    cin>>s1;
    root=new Tman();
    has[s1]=root;
    root->name=s1;
    while(cin>>s1){
        if(s1=="print"){
            print(0,root);
            for(int i=0;i<60;i++){
                cout<<"-";
            }
            cout<<endl;
        }
        else if(s1=="fire"){
            cin>>s2;
            fire(s2);
        }
        else{
            cin>>s2;
            cin>>s2;
            hires(s1,s2);
        }
    }
}
int main(){
    //freopen("1.txt","r",stdin);
    solve();
    return 0;
}

鏈表學的真是菜啊。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章