圖的儲存結構

Haskei發表於2016-11-06
1.鄰接矩陣
用一個n×n的二維陣列來儲存,例如node[i][j],代表的是從點i到點j的距離為node[i][j],缺點是無法儲存重邊的情況,比如從i點到j點有兩條路可以走,一條路的長度為3,一條路的長度為5

程式碼實現

    int n,m,node;  
    scanf("%d%d",&n,&m);//n點的個數,m邊的條數   
      
    //建立城市之前的關係圖,自身到自身距離為0,兩個城市無法到達則為INF   
    for(int i=1;i<=n;i++)  
        for(int j=1;j<=n;j++)  
        {  
            if(i==j)  
                city[i][j]=0;  
            else city[i][j]=INF;  
        }  
    int x,y,r;  
    //輸入題目給出的兩個城市之間的距離   
    for(int i=1;i<=m;i++)  
    {  
        scanf("%d%d%d",&x,&y,&r);  
        city[x][y]=r;  
    } 


2.向前星
讀入每條邊的資訊,將邊存放在陣列中,把陣列中的邊按照起點順序排序
優點是應對點非常多的情況,可以儲存重邊,但是不能直接判斷任意來兩個頂點之間是否有邊,而且排序要浪費一些時間

程式碼實現

#include<iostream>
#include<cstring>
#include<string>    
#include<queue>    
#include<algorithm>    
#include<map>    
#include<iomanip>    
#define INF 99999999    
#define MAX 20   
using namespace std;    
//向前星 
int head[MAX];//用來記錄儲存起點為i的第一條邊的位置 ,也就是說在排好序以後,該起點的第一條邊在這個邊的陣列中的位置 
              //利用head陣列也是為了以後查詢方便,可以查詢從某一頂點出發的所有的邊 
struct Node{
	int from;//邊的起點 
	int to;//邊的終點 
	int w;//邊的權值 
};

Node edge[MAX];//建立一個邊的集合的陣列 

bool cmp(Node a, Node b)//比較函式 
{
	if(a.from==b.from && a.to==b.to) return a.w<b.w;//如果兩條邊的起點和終點相同,則按照權值從小到大排序 
	if(a.from==b.from) return a.to<b.to;//如果兩條邊的起點相同,但是終點不相同,按照重點的大小從小到大排序 
	return a.from<b.from;//如果兩條邊的起點不相同,則按照起點的大小,從小到大排序 
}

int main()
{
	int n,m;//n代表點的個數,m代表邊的條數 
	cin>>n>>m; 
	for(int i=0;i<m;i++)
		cin>>edge[i].from>>edge[i].to>>edge[i].w;
		
	sort(edge,edge+m,cmp); //按上面的排列規則,把邊按順序排好 
	memset(head,-1,sizeof(head));
	head[edge[0].from]=0;//第一條邊的位置為0
	
	for(int i=1;i<m;i++)
		if(edge[i].from!=edge[i-1].from) head[edge[i].from]=i;//如果該起點與上一條邊的起點不相同,則是一個新的點 
	                                                         //所以該新的起點的第一條變的的位置就要記錄下來 
	 //遍歷程式碼
	for(int i=1;i<=n;i++)
	{
	 	for(k=head[i];edge[k].from==i && k<m;k++)//k為頂點為i的第一條邊的位置,如果該條邊的起點不是i,或者該條邊的 
	 	{                                        //位置超出了最大位置,這些邊的位置最大為m-1,因為是從0開始算起的 
	 		cout<<edge[k].from<<" "<<edge[k].to<<" "<<edge[k].w<<endl;
		}
	} 
	return 0;
}
/*測試資料  
6 9 
1 2 1 
1 3 12 
2 3 9 
2 4 3 
3 5 5 
4 3 4 
4 5 13 
4 6 15 
5 6 4 

 
程式執行效果 
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4  
*/  


3.鄰接表
鄰接表就是以每一個節點為起點,分別建立一個單連結串列,用來連結與它直接相鄰的點,通過鄰接表可以訪問與每個節點直接相連的邊與頂點
(1)利用vector動態實現


#include<iostream>
#include<cstring>
#include<string>    
#include<queue>    
#include<algorithm>    
#include<map>    
#include<iomanip>    
#define INF 99999999    
#define maxn 20   
#include<vector>
using namespace std;   

struct node{
	int to;//終點
	int w;//權值
}; 

vector<node> Map[maxn];

int main()
{
	int n,m;
	node t;
	cin>>n>>m;
	for(int i=0;i<m;i++)
	{
		int from;//form為鄰接表的頭 ,也就是鄰接表的邊的起點
		cin>>from>>t.to>>t.w;
		Map[from].push_back(t);
	}
	cout<<endl;
	//遍歷每一個點的鄰接表 
	for(int i=1;i<=n;i++)
	{
		for(vector<node>::iterator k=Map[i].begin();k!=Map[i].end();k++)//遍歷鄰接表的每一條邊 
		{
			t=*k;
			cout<<i<<" "<<t.to<<" "<<t.w<<endl;
		}
	}
	return 0;
}
/*測試資料  
6 9 
1 2 1 
1 3 12 
2 3 9 
2 4 3 
3 5 5 
4 3 4 
4 5 13 
4 6 15 
5 6 4 

 
程式執行效果 
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4  
*/  


(2)利用陣列靜態實現(鏈式向前星)


鏈式向前星的建圖效率非常高,讀入結束,建圖就結束,時間複雜度為O(m),空間上使用了兩個陣列,空間複雜度為
O(m+n)
鏈式向前星的優點在於除了必要的邊資訊的儲存空間外,只需要非常少的額外空間即可實現,程式碼和原理都比較簡單,可以儲存重邊,還可以應付點和邊的數量很大的情況,與動態建表相比沒有記憶體管理,更安全,因該說除了不能直接用起點和終點確定是否有邊以外,鏈式向前星幾乎是完美的(以上語句摘自《圖論及應用ACM-ICPC程式設計系列》)

用鄰接表來儲存圖的結構,構圖的時間複雜度為O(m),遍歷每一條邊的時間複雜度也為O(m)

如果一個圖是稀疏圖的話,m要遠小於n,因此稀疏圖用鄰接表來儲存要比鄰接矩陣要好得多

#include<iostream>
#include<cstring>
#include<string>    
#include<queue>    
#include<algorithm>    
#include<map>    
#include<iomanip>    
#define INF 0x3f3f3f3f 
#define maxn 20   
#include<vector>
using namespace std;   

struct node{
	int to;
	int w;
	int next;
}; 

int main()
{
	int n,m,from;//form代表鄰接表的第一個點,也就是起點 
	node Edge[maxn];//node型別的存放邊的陣列 
	int head[maxn];//用來存放第i個點所對應的第一條邊在Edge陣列裡面的位置 
	cin>>n>>m;
	memset(head,-1,sizeof(head));
	//相當於連結串列的前插,就是每次插入一條邊都是插入到該連結串列的第一位置,比如連結串列一開始為1->2->3,後來插入了4,就變成了
	//4->1->2->3 
	for(int i=0;i<m;i++)
	{
		cin>>from>>Edge[i].to>>Edge[i].w;
		Edge[i].next=head[from];
		head[from]=i;
	} 
	cout<<endl;
	//遍歷每一個點的鄰接表 
	for(int i=1;i<=n;i++)
	{
		for(int j=head[i];j!=-1;j=Edge[j].next)//-1代表該鄰接表已經沒有下一條邊了,該點的鄰接表遍歷完成 
			cout<<i<<" "<<Edge[j].to<<" "<<Edge[j].w<<endl;
	}
	return 0;
}
/*測試資料  
6 9 
1 2 1 
1 3 12 
2 3 9 
2 4 3 
3 5 5 
4 3 4 
4 5 13 
4 6 15 
5 6 4 

 
程式執行效果 
1 3 12
1 2 1
2 4 3
2 3 9
3 5 5
4 6 15
4 5 13
4 3 4
5 6 4  
*/  



相關文章