1. 程式人生 > >樹的一些基礎東西

樹的一些基礎東西

時間戳 str class family 樹的重心 過程 lock void 容易

1、時間戳:

按照dfs的過程,將每個節點第一次被訪問的順序(v[x]被標記為1時),依次給這n個節點標記1~n的數字即該節點的時間戳。

代碼:

1 void dfs(int x){
2     v[x]=1;dfn[x]=++cnt;  //dfn時間戳,cnt初始為0
3     for(int i=head[x];i;i=next[i]){
4         int y=ver[i];
5         if(v[y])continue;
6         dfs(y);
7     }
8 }

2、樹的dfs序

第一次訪問到某個節點時,記錄其一次編號,回溯時再記錄一次它的編號,最後產生2n的節點序列就是樹的dfs序。

性質:某節點x從第一次被標記在節點序列中到第二次被標記之間的節點序列就是以x為根節點的子樹的dfs序

代碼:

 1 void dfs(int x){
 2     a[++m]=x; //a數組存儲dfs序
 3     v[x]=1;  //記錄點是否被訪問過
 4     for(int i=head[x];i;i=next[i]){
 5         int y=ver[i]; 
 6         if(v[y])continue;
 7         dfs(y);
 8     }
 9     a[++m]=x;
10 }

3、樹的深度

樹中各個節點的深度是自上而下的統計信息,其中根節點深度為0,其它節點深度即到根節點的層數。往下的子節點深度依次+1,即若當前所在節點x的深度為dep[x],則它的子節點y的深度為dep[y]=dep[x]+1;

代碼:

1 void dfs(int x){
2     v[x]=1;
3     for(int i=head[x];i;i=next[i]){
4         int y=ver[i];
5         if(v[y])continue;
6         d[y]=d[x]+1;   //父節點向子節點更新深度
7 dfs(y); 8 } 9 }

4、樹的子樹大小和重心

樹的子樹大小是自下而上統計的信息,子樹大小即以某個節點x為根節點的子樹的節點個數,一般用size[x]表示。容易想到,若x的子節點為:y1、y2……yn,則size[x]=size[y1]+size[y2]+…+size[yn];

樹的重心。指的是對於一個節點x,從樹中刪去它,產生的若幹個不想連的部分中節點數最多的一顆樹的節點數size記為maxn,令maxn取最小值的節點x即一顆樹的重心。

代碼:

void dfs(int x){
    v[x]=1;size[x]=1; //size存儲子樹大小
    int max_part=0;   //刪除x節點後最大子樹的大小
    for(int i=head[x];i;i=next[i]){
        int y=ver[i];
        if(v[y])continue;
        dfs(y);
        size[x]+=size[y];
        max_part=max(max_part,size[y]);
    }
    max_part=max(max_part,n-size[x]); //n為整棵子樹節點總數
    if(max_part<ans){ans=max_part;pos=x;} //ans記錄重心對應的max_part,pos記錄重心
}

5、圖的連通塊的個數和劃分

dfs可以求出森林(即多棵不連通的樹)中樹的個數,並將節點劃分到每棵樹中。思路就是不停對沒有劃分的節點x進行dfs遍歷,就會訪問所有與x連通的點並對它們劃分,直到所有點均被劃分為止。

代碼:

 1 void dfs(int x){
 2     v[x]=cnt;
 3     for(int i=head[x];i;i=next[i]){
 4         int y=ver[i];
 5         if(v[y])continue;
 6         dfs(y);
 7     }
 8 }
 9 int main()
10 {
11     for(int i=1;i<=n;i++)
12         if(!v[i]){cnt++,dfs(i);} //cnt即連通塊個數
13 }

樹的一些基礎東西