樹、簡單圖的儲存方法——鄰接矩陣 鄰接表 和 鏈式前向星 學習筆記
ps:樹是一種特殊的圖,樹有自己特殊的儲存方式,圖的儲存方式都能應用於樹。
對於圖、樹來講,一般給出一個n表是有n個節點(標號1~n)m個二元組(a,b)表示ab之間有一條邊。這樣就能確定一個圖。
對於樹來講沒有環,所以m=n-1
part one、鄰接矩陣
鄰接矩陣的優點是可以O(1)查出兩點之間有沒有邊,缺點是無法高效的查詢某個點的所有邊,且對於稀疏圖來講浪費了大量的空間(存了很多0)。
使用mp[maxn][maxn]二維陣列儲存,mp[i][j]=0表示節點i,j之間不直接相連,為1表示有一條邊相連(當然對於簡單圖來說只能為1和0)。
int a,b,n,m; cin>>n>>m; memset(mp,0,sizeof(mp));//清0 for(int i=0;i<n;i++){ scanf("%d%d",&a,&b);//a和b之間有邊 mp[a][b]=mp[b][a]=1;//無向圖兩個都要賦值 }
這樣圖就存好並可以使用了。
part two、鄰接表
鄰接表可以最高效率地查詢某個點的所有邊,但是無法高效地查詢兩個點之間是否有邊。
使用連結串列儲存(實際應用時為了方便用vector):只記錄與點i有邊的點,這樣相當於記錄邊。相當於弱化的十字連結串列(多用了空間)
vector<int>mp[maxn]; cin>>n>>m; for(int i=1;i<=m;i++){ scanf("%d%d",&a,&b); mp[a].push_back(b); mp[b].push_back(a); }
part three、鏈式前向星
前向星也是儲存邊。個人認為前向星只是鄰接表的另一種寫法,陣列模擬連結串列空間分配(記憶體池)的一種寫法。
好像單純學前向星沒有什麼創新的知識,但由於看網上程式碼尤其是樹鏈剖分和lca很多都用鏈式前向星的寫法,還是老老實實理解一下前向星書寫套路。
其實照著鄰接表的思路理解鏈式前向星就很容易了(吐槽一下為什麼起一個這麼貌似高大上的名字,老老實實叫鄰接表不好嗎)
const int maxn = 10005; const int maxm = 1000005;//edge using namespace std; int n; struct node {//儲存邊的資料結構,to是點,next是指標。 int to, next; // int value ,from, }; node edge[maxm];//儲存邊的陣列,兼任記憶體池。 int box[maxn];//記錄節點i鄰接表第一個成員指標,使用頭插法。 int ecnt;//邊的個數 inline void _make_map(int from, int to) { //建立從from到to的一條單向邊,若無向圖則正反建立兩條。 頭插法 edge[ecnt].to = to;//to 節點 edge[ecnt].next = box[from];//同節點下該邊下一條邊 box[from] = ecnt++;// 節點from的第一條邊 } inline void make_map(int from, int to)//雙向邊 { _make_map(from, to);//正反向兩次雙向邊 _make_map(to, from); } int main() { while (scanf("%d", &n) != EOF) { ecnt = 0; int i; int u[100], v[100]; // 儲存邊 for (i = 0; i < n; i++) { scanf("%d%d", &u[i], &v[i]); make_map(u[i], v[i]); } } return 0; }