圖論基礎知識(2)-三種存圖法

Markdown真是不知道比TinyMCE高到哪裏去 ##前言 若是咱們要用電腦去處理一幅圖的化,首先咱們得想辦法把這副圖給存儲起來。存圖並非像存數字那麼簡單開幾個變量就能實現的,存圖通常來說有3種方法,它們分別是node

  1. 鄰接矩陣
  2. 鄰接表
  3. 鏈式前向星

那麼咱們先從最容易理解的鄰接矩陣來看吧 ###鄰接矩陣 鄰接矩陣的思想是在矩陣中存儲每一條邊的起點和終點來達到存儲一幅圖的目的,能夠看下面的這幅圖c++

這幅圖裏面一共有6個點,因此咱們建立一個6*6的0矩陣,而且對於每一條邊,咱們找出它的起點和終點,而且在矩陣的[x][y]處的0改爲1.數組

上面這幅圖在鄰接矩陣裏面應該這麼表示:網絡

打個比方,能夠觀察這幅圖,咱們發現對於一條鏈接着4和5的邊,矩陣裏面的(4,5)和(5,4)位置上的0都變成了1,這就是鄰接矩陣的存圖方法,對於圖中的任意一條鏈接着u和v的邊,咱們在矩陣裏面的(u,v)位置打一個標記,表示有一條邊是從u到v的,同時,由於原圖是一個無向圖,因此對於每一組(u,v),它們在矩陣上的(v,u)位置也要標記。若是對於有向圖來講就不必了spa

順便說一句,<b>通常</b>在比賽裏面咱們輸入一個圖的形式都是這樣的:code

6 6 (表示這幅圖一共有6個點和6條邊) 1 4 1 3 1 5 2 4 4 5 3 6 因此在寫程序的時候要注意一下。 ###鄰接矩陣的弊端 雖說鄰接矩陣很是容易理解,可是它也有很大的缺陷:鄰接矩陣消耗的空間太大了。若是咱們存儲一個稀疏圖($|E|$遠小於$|V|^2$),那麼咱們的矩陣實際上大部分都是0,也就是說咱們佔用了過多的空間。實際上,鄰接矩陣在數學方面是一種很好的存圖方式,可是涉及到計算機的時候就須要考慮時間和空間,因此鄰接矩陣在咱們實際寫程序的時候仍是少用(固然對於數據量比較小的題目仍是能夠用一用的)blog

下面給出鄰接矩陣的代碼:(仍是很是簡單的)get

#include <bits/stdc++.h>
using namespace std;
const int maxn=1926;
int n,m,g[maxn][maxn];
int main(void){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d %d",&u,&v);
		g[u][v]=g[v][u]=1;
	}
	for(int i=1;i<=n;i++){
		printf("%d: ",i);
		for(int j=1;j<=n;j++){
			if(g[i][j]) printf("%d ",j);
		}
		printf("\n");
	}
}

###鄰接表 既然鄰接矩陣被咱們淘汰了,咱們就須要找一個能夠替代鄰接矩陣的東西,它就是鄰接表 咱們能夠從上面的代碼裏面發現,就算咱們一個點u只鄰接了1條邊,咱們的程序仍是得掃描g[u][1~n],由於咱們一個點對應着它和n個點之間的信息。那麼,若是咱們只存儲邊的信息不久好了嗎?鄰接表就這麼誕生出來了 我的認爲比起語言敘述,畫一個圖更能體現出鄰接表的思想源碼

仍是這幅圖: 數學

他的鄰接表長這樣:

(字醜因此畫了好幾遍)

咱們能夠從表裏面發現,咱們再也不是單純的存儲0和1了,由於鄰接表存儲的是邊。就上圖來講,第一列存儲的是1~N個點的編號,而後咱們再看第一行,1後面的3,4,5意思是和1相鄰接的第一條邊的終點是3,和1相鄰接的第二條邊的終點是4,和1相鄰接的第三條邊的終點是5。這樣的化咱們的空間就節省了許多,同時訪問的速度也快了許多。

能夠試一試這道題: https://www.luogu.com.cn/problem/P3916 很是基礎的圖論練手題(比打着普及-的標籤還要你套最短路的水題不知道高到哪裏去),我的以爲很適合練手 代碼:

#include <bits/stdc++.h>
using namespace std;
const int maxn=100010;
struct edge{
	int to;
	edge(int to_){
		to=to_;
	}
};
vector<edge> gpe[maxn];
int ans[maxn],m,n;
void dfs(int u,int d){
	if(ans[u]) return;
	ans[u]=max(ans[u],d);
	for(int i=0;i<gpe[u].size();i++){
		int v=gpe[u][i].to;
		dfs(v,d);
	}
} 
int main(void){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d %d",&u,&v);
		gpe[v].push_back(edge(u));
	}
	for(int i=n;i>=1;i--) dfs(i,i);
	for(int i=1;i<=n;i++){
		printf("%d ",ans[i]); 
	}
}

AC記錄:

###鏈式前向星

我我的不多用到鏈式前向星這種方法,可是周圍的dalao都在用,也許這就是我和dalao們的差距吧( emmm...鏈式前向星其實和鄰接表差很少,可是因爲咱們在寫鄰接表的時候常常用到vector, 有些dalao寫的stl源碼剖析中寫了,vector會先開2倍空間,而且還因爲每一個人的寫法不一樣,有些鄰接表的寫法會致使效率上略遜於鏈式前向星,可是我目前還沒遇到卡鄰接表的題目。。。

鏈式前向星有2個組成部分,edge數組和head數組 edge[i]裏面有to,w,next三個變量。to存儲的是這條邊的終點,w存儲的是邊權,next存儲的是在edge數組裏面,此時u所鄰接的下一條邊的下標(好繞人。。。),而後head[u]數組存儲了對於某一個點u,它在edge數組裏面的下標 因此咱們能夠根據以上的描述寫出鏈式前向星的代碼:

#include <bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int head[N];
int cnt=0;
struct node{
    int from;
    int to;
    int w;
}edge[N];
void add(int u,int v,int w){
    edge[cnt].w=w;
    edge[cnt].to=v;
    edge[cnt].from=head[u];
    head[u]=cnt++;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    memset(head,0,sizeof(head));
    while(m--){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    int start;
    scanf("%d",&start);
    for(int i=head[start];i!=0;i=edge[i].from){
        cout<<start<<"->"<<edge[i].to<<" "<<edge[i].w<<endl;
    }
    return 0;
}

###總結 多是由於我太蒻了,因此鏈式前向星這部分我講的不是很清晰,感興趣的化能夠在網上自行搜索相關知識。。。 建圖方面還有一些其餘的知識(好比說網絡流中如何建反邊)還沒提到,這只是一些比較基本的知識。 若是隻是單純考察建圖,我只找到了那一道。。。

相關文章
相關標籤/搜索