建立圖的三種方法(鄰接矩陣+鄰接表+十字連結串列)
一、鄰接矩陣
採用矩陣的方式來描述圖中的連線各非連線關係,若不能連上用無窮大或者0來表示,但是如果邊很稀少,頂點很多,那麼將會有很大的浪費。同時,這個矩陣可以同時刻畫有向圖和無向圖,無向圖就是把有向圖根據對角線對稱即可。
1、思想:建立一個結構體,它包含了這個圖所應該具備的頂點、邊的條數、矩陣(二維陣列的資訊),同時因為邊有三個資訊,權值,連線兩個頂點v1,v2,所以也用一個結構體表示
2、用到的函式:
MGraph CreateGraph_matric(int nvNum);
void InsertEdge_matric(MGraph Graph,Edge E);
MGraph biuldGraph_matric()
3、過程
(1)從檔案中讀入nv
(2)建立一個具有nv個頂點的圖:
CreateGraph_matric(nv)
申請一個鄰接矩陣圖的空間,先把邊設定成0,再用兩個for迴圈遍歷所有可能的邊,每條邊設定成無窮大(初始化)
(3)從檔案中讀入邊的資訊,讀入邊的連線資訊(權值+兩個被邊連線的頂點),把他們的資訊賦給Edge的邊的結構體,再把這條邊插進圖中
InsertEdge_matric(Graph,E);
在插入函式中,說明:哪兩個頂點間的權值是多少,即可
Graph->G[E->v1][E->v2]=E->weight;
二、鄰接表
1、結構體
/*鄰接點的定義*/
typedef int weightType;
typedef char dataType;
struct AdjvNode{
Vertex Adjv;//鄰接點的下標
weightType weight;
struct AdjvNode *next;
};
typedef struct AdjvNode *PtrToAdjvNode;
/*頂點表頭結點的定義*/
struct Vnode{
PtrToAdjvNode firstEdge;//邊表頭指標
dataType data;
};
//開闢頂點結點的陣列
typedef struct Vnode AdjList[MaxVertexNum];
/*鄰接表-圖的定義*/
struct GNode_AL{
int nv;
int ne;
AdjList G;//頂點表
};
typedef struct GNode_AL *LGraph;
鄰接表的結構是這樣構建的:
整個圖應該由頂點表+邊表構成,怎麼把他們連線起來?
(大框架)(裡邊包含頂點表,邊數,頂點數)->(頂點表)(又包含 頂點內容+指向邊表的指標)->(指標引出邊表)(邊表包含 :被指向邊的頂點+權值+指向該條連結串列的下一個指標)
2、用到函式
同鄰接矩陣(3個)
3、建立過程
同鄰接矩陣。
初始化的時候有些區別。剛開始的時候,除了申請一個圖的空間,還給每個頂點表也申請了空間,其實根本不用,因為我們建立的頂點表是用陣列來建立的,已經申請了空間。把每個頂點表的頭指標賦值為NULL。
//建立鄰接表的圖
LGraph biuldGraph_adjacencyList(){
LGraph Graph;
Edge E;
int nv,i;
FILE*fp;
if((fp=fopen("E://資料結構//圖 實驗//matric1.txt","r"))==NULL)
{
printf("fail to open!");
exit(0);
}
fscanf(fp,"%d\n",&nv);//頂點個數
Graph=CreateGraph_adjacencyList(nv);//初始化有nv個頂點但沒有邊的圖
fscanf(fp,"%d\n",&Graph->ne);//讀入邊數
if(Graph->ne!=0){
E=(Edge)malloc(sizeof(struct ENode));
for(i=0;i<Graph->ne;i++){
fscanf(fp,"%d %d %d\n",&E->v1,&E->v2,&E->weight);//讀入邊的資訊
InsertEdge_adjacencyList(Graph,E);//把這條邊插入矩陣資訊中
}
}
//讀入頂點的資料
for(i=0;i<Graph->nv;i++){
fscanf(fp,"%c",&Graph->G[i].data);//讀入A、B、C頂點的資料
}
return Graph;
}
//初始化有nv個頂點但沒有邊的圖
LGraph CreateGraph_adjacencyList(int nvNum){
Vertex v;
LGraph Graph;
//為這個圖申請空間
Graph =(LGraph)malloc(sizeof(struct GNode));
Graph->nv=nvNum;
Graph->ne=0;
for(v=0;v<Graph->nv;v++)
Graph->G[v].firstEdge=NULL;
return Graph;
}
插入的時候,每一行的插入都是往前插
//把這條邊插入鄰接表資訊中
void InsertEdge_adjacencyList(LGraph Graph,Edge E){
PtrToAdjvNode newnode;//定義一個表頭結點
//建立一個v2鄰接點
newnode=(PtrToAdjvNode)malloc(sizeof(struct AdjvNode));
newnode->Adjv=E->v2;//被連線到的點
newnode->weight=E->weight;
//將v2插入v1表頭
newnode->next=Graph->G[E->v1].firstEdge;
Graph->G[E->v1].firstEdge=newnode;
}
如何理解將v2插入v1表頭
三、十字連結串列
1、結構體
/*十字連結串列的結構體*/
//結點結構
struct ArcNode{
int tailvex,headvex;//弧的頭和頂點的位置
struct ArcNode *hlink,*tlink;//頭尾指標
weightType weight;
};
typedef struct ArcNode *Arcbox;
//頭結點
struct XNode{
dataType data;
Arcbox firstIn,firstOut;
};
typedef struct XNode xlist[MaxVertexNum];
/*十字連結串列-圖的定義*/
struct OLGNode{
int nv;
int ne;
xlist G;//鄰接表
};
typedef struct OLGNode *OLGraph;
結點表和邊表都多了一個方向的指標
2、過程
這個一模一樣,不寫了
OLGraph biuldGraph_arc(){}
下面這個for語句是要對兩個頭尾指標都要賦初值NULL,但是沒有用括號,導致另一個指標瞎指,找了好久才發現,一定要細心!!
//初始化有nv個頂點但沒有邊的圖
OLGraph CreateGraph_arc(int nvNum){
Vertex v;
OLGraph Graph;
//為這個圖申請空間
Graph =(OLGraph)malloc(sizeof(struct OLGNode));
Graph->nv=nvNum;
Graph->ne=0;
/*
for(v=0;v<Graph->nv;v++)
Graph->G[v].firstIn=NULL;
Graph->G[v].firstOut=NULL;出錯原因!!
*/
for(v=0;v<Graph->nv;v++){
Graph->G[v].firstIn=NULL;
Graph->G[v].firstOut=NULL;
}
return Graph;
}
//把這條邊插入鄰接表資訊中
void InsertEdge_arc(OLGraph Graph,Edge E){
Arcbox A;
A=(Arcbox)malloc(sizeof(struct ArcNode));//為結點申請空間
A->weight=E->weight;
A->headvex=E->v2;
A->tailvex=E->v1;
A->tlink=Graph->G[A->tailvex].firstOut;//出去的指標解決了
Graph->G[A->tailvex].firstOut=A;//把出去的邊連線上
//解決firstIn,hlink的指標
A->hlink=Graph->G[A->headvex].firstIn;
Graph->G[A->headvex].firstIn=A;
}
firstout-tlink firstin-hlink