數據結構:樹


1.數據結構導論

1.1.基礎定義

樹是一種數據結構,它是由n(n>=1)個有限結點組成一個具有層次關系的集合。把它叫做“樹”是因
為它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。
在這種層次結構中有一個結點具有特殊的地位,這個結點稱為該樹的根結點,或稱為樹根。

這是一顆樹

術語 描述
根結點 每棵樹都有一個根結點 50為這棵樹的根節點
子節點 一個節點的后繼節點被稱為子節點 50的子節點為27,75
父節點 若一個節點含有子節點,則這個節點稱為其子節點的父節點 50是27,75的父節點
兄弟節點 具有相同父節點的不同節點 61和85是兄弟節點
節點的度 一個節點含有的子節點的個數稱為該節點的度 75的度為2,61的度為1
葉節點 度為0的節點稱為葉節點 67、81都是葉子節點
樹的深度 樹中節點的最大層次 最深的節點為67,深度為4
節點的祖先 從根到該節點所經分支上的所有節點 50,75,61,71都是67的祖先
子孫 所有子節點以及子節點的子節點以及… 所有節點都是50的子孫
子樹 以某節點為根的樹 以75為根的子樹包括7個節點
父子節點直接存在一條連邊 50和27之間有條邊

1.2.樹的特性

一棵樹中任意兩個結點有且僅有唯一的一條路徑連通。
一棵樹如果有n個節點, 那么它一定有n-1條邊。
在一棵樹中添加一條邊將會構成一個回路。
一棵有 n個節點的無向樹,所有節點的度數和為2× (𝑛 − 1) 。

樹形結構以樹和二叉樹最為常用,直觀來看,樹是以分支關系定義的層次結構。樹形結構
中元素之間有着明顯的層次關系,每一個元素可以和下層的多個元素相關, 但只能和上層
中一個元素相關。

1.3.樹與鏈表

鏈表是一棵特殊的樹。我們可以將鏈表的頭節點看作樹的根。樹的每個節點可以有多個
next ,我們稱之為子節點。所以鏈表是一棵每個節點都只有一個子節點的樹。

鏈表
頭節點 根結點
只有一個后繼節點(next) 有一個或多個子節點(child)
雙向鏈表包括前驅結點(pre) 除了根結點外,每個節點都有唯一的父節點(parent)
一對一關系 一對多關系

1.4.樹的基本操作-樹的存儲與創建

在創建一棵樹的時候, 使用什么方法去存儲呢?
可以采用與鏈表類似的方法。但由於子節點的數量不確定, 因此我們想到用vector來存儲
樹的子節點。
在一般樹中,子節點的數量沒有限制,所以常用的存儲方法是使用vector數組G, G[0]保
存編號為0的頂點連接到的所有頂點, 由於n個結點的樹只有n-1條邊, vector數組實際占
用的空間為O(n) 。
每讀到一條邊(u,v) ,如果不知道誰是父親誰是兒子,我們可以先在G[u]中添加一個v ,再
在G[v]中添加一個u 。
如果知道父子關系,可以只在父節點中添加兒子,而不用將邊保存兩份。

vector<int> G[100005]; //每個vector用來記錄所有子節點的編號
int n;
int main()
{
    scanf("%d", &n);
    for(int i = 1; i < n; i++){
    int u, v;
    scanf("%d %d",&u, &v);
    G[u].push_back(v);
    G[v].push_back(u);
    }
    return 0;
}

1.5.刪除樹的點和邊

刪除節點u時需要刪除該節點的所有邊。
Del(當前節點u){
    for(u有連邊的節點v){
        從G[v]中刪除u;
    }
    清空G[u];
}
刪除邊(u,v) 。
Del(邊(u,v)){
    從G[v]中刪除u;
    從G[u]中刪除v;
}

1.6.樹的遍歷

遍歷樹的方式有很多,最常用的方式是DFS 。
DFS(當前節點u){
    for(u的所有子節點v){
        DFS(v);
    } 
}
如果是在不知父子的情況下,保存了雙向邊,那么需要做如下處理。
DFS(當前節點u, 父節點p){
    for(u有連邊的節點v){
        if(v不是p)
            DFS(v, u);
    } 
}
遍歷方式對比
數組 從下標0開始遍歷
鏈表 從頭節點開始遍歷
從根節點開始遞歸遍歷

1.7.例題:葉子節點的數量

給出一棵n個節點的樹,節點編號為1-n(根節點編號為1),求這棵樹葉子結點的數量。
例如:
1─2─4─5
└─3
其中3和5是葉子節點,輸出2。
1.7.1.輸入

第一行:1個數n(1 < n <= 1000),表示樹的節點數量。
后面n-1行:每行2個數x y,表示節點x是節點y的父節點(1 <= x, y <= n)。

1.7.2.輸出

輸出1個數,表示這棵樹有多少個葉子節點。

1.7.3.輸入樣例

5
1 2
1 3
2 4
4 5

1.7.4.輸出樣例

2

1.7.5.分析
可以用一個bool數組t[i]來存儲每一個節點,t[i]表示第i個節點是否有子節點,如果沒有,i就是葉子節點。但要注意輸入 n-1次,判斷t[i]是否為false時要循環n次。t數組要開在外邊,否則在main函數里要memset初始化一下。
1.7.6.代碼

#include <bits/stdc++.h>
using namespace std;
bool t[1010]; 
int main() {
    int n, ans = 0;
    cin >> n;
    for(int i = 1; i <= n - 1; i++) {
        int a, b;
        cin >> a >> b;
        t[a] = true;
    }
    for(int i = 1; i <= n; i++) if(!t[i]) ans++;
    cout << ans << endl;
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM