1. 程式人生 > >圖的概念和基於鄰接矩陣的實現

圖的概念和基於鄰接矩陣的實現

這一篇簡單介紹下一種更為複雜的資料結構------圖,圖是一種比線性表和樹更為複雜的資料結構。圖結構是研究資料與資料之間多對多的關係。

一、圖的基本概念

1、一個圖定義為一個偶對(V,E),記為G=(V,E);其中V是頂點的非空有限集合,記為V(G),E是無序集V&V的一個子集,記為E(G),其元素是圖的弧。

2、弧:表示兩個頂點之間存在一個關係,用頂點偶對<v,w>表示,通常根據頂點偶對將圖分為有向圖和無向圖。

3、有向圖:若圖的關係集合E(G)中,頂點偶對<v,w>的v和w之間是有序的,稱圖G是有向圖。在有向圖中,若<v,w>屬於E(G),表示從頂點v到頂點w有一條弧。

4、無向圖:若圖的關係集合E(G)中,頂點偶對<v,w>的v和w之間是無序的,稱圖G是無向圖。在無向圖中,E(G)是對稱的,則用無序對(v,w)表示v到w的一條邊,即(v,w)和(w,v)表示的是同一條邊。

5、完全有向圖:對於有向圖,若圖的頂點數是n,用e表示弧的數目,e=[0,n(n-1)],具有n(n-1)條弧的有向圖稱為完全有向圖。

6、權:與圖的邊和弧相關的數。權可以表示從一個頂點到另一個頂點的距離或耗費。

7、子圖和生成子圖:設有兩個圖G1和G2,G1的頂點集是G2的頂點集的子集,G1的弧集是G2的弧集的子集,則稱G1是G2的子圖;若G1和G2的頂點集相等,G1的弧集是G2的弧集的子集,則稱G1是G2的生成子圖。

8、頂點的鄰接:對於無向圖G=(V,E),若v和w之間存在邊,則稱v和w互為鄰接點。邊依附於點v和w。對於有向圖G=(V,E),若v和w之間存在有向弧,稱頂點v鄰接到w,稱頂點w鄰接自v,弧與頂點v和w相關聯

9、度、出度、入度、路徑(有向路徑)、路徑長度、迴路(環)

10、連通圖、非連通圖、圖的連通分量(非連通圖的極大連通子圖)-----無向圖而言

11、強連通圖、非強連通圖、強連通分量-------有向圖而言

12、生成樹:一個連通圖(無向圖)的生成樹是一個極小連通子圖,包含全部n個頂點和構成樹的n-1條邊。生成森林:由若干棵有向樹組成,含有圖中全部頂點,有向樹是隻有一個頂點的入度為0,其他頂點的入度為1的有向圖。

13、網:帶權的連通圖

二、圖的儲存結構

1、鄰接矩陣(陣列)表示法:包括圖的建立、頂點定位、插入頂點、插入弧等

//圖的鄰接矩陣表示
#define INFINITY 1000
#define MAX_VEX 30 //最大頂點數目
using namespace std;
typedef string GraphKind;//有向,無向,帶權有向,帶權無向;(DG,AG,WDG,WAG)
//typedef ArcType AdjType;
typedef char VexType;
typedef int ArcValType;
typedef int ArcInfoType;
//弧或邊的結構定義
typedef struct ArcType
{
    VexType vex1,vex2; //依附的兩個頂點;
    ArcValType ArcVal;//權值
    ArcInfoType ArcInfo;// 其他資訊
}ArcType;
typedef ArcType AdjType;
//圖的定義
typedef struct MGraph
{
    GraphKind kind;
    int vexnum,arcnum; //圖的當前頂點數和弧數
    VexType vexs[MAX_VEX];
    AdjType adj[MAX_VEX][MAX_VEX];//弧集
}MGraph;
//create graph
void Create_Graph(MGraph *G)
{
    cout<<"請輸入圖的種類標誌:\n"<<endl;
    string s;
    cin>>s;
    G->kind=s;
    G->vexnum=0;
}
//圖的頂點定位,即確定一個頂點在vexs陣列中的下標
int Locate_Graph_Vex(MGraph *G,VexType *vex)
{
    for(int k=0;k<G->vexnum;++k)
        if(G->vexs[k]==*vex)
        return k;
    return -1;
}
//向圖中增加頂點
int AddVex_Graph(MGraph *G,VexType *vex)
{
    int k;
    if(G->vexnum>=MAX_VEX){
        cout<<"graph is overflow!"<<endl;
        return -1;
    }
    if(Locate_Graph_Vex(G,vex)!=-1)
    {
        cout<<"the vex has existed!"<<endl;
        return -1;
    }
    k=G->vexnum;
    G->vexs[G->vexnum++]=*vex;
    if(G->kind=="DG" || G->kind=="AG") //無權值
    {
        for(int j=0;j<G->vexnum;j++)
        {
            G->adj[j][k].ArcVal=G->adj[k][j].ArcVal=0;
        }
    }
    else
    {
        for(int j=0;j<G->vexnum;j++)
        {
            G->adj[j][k].ArcVal=INFINITY;
            G->adj[k][j].ArcVal=INFINITY;
        }
    }
    return 1;
}
//
int AddArc_Graph(MGraph *G,ArcType *arc)
{
    int k,j;
    k=Locate_Graph_Vex(G,&arc->vex1);
    j=Locate_Graph_Vex(G,&arc->vex2);
    if(k==-1 || j==-1)
    {
        cout<<"the vexs has not existed!"<<endl;
        return -1;
    }
    if(G->kind=="DG" || G->kind=="WDG") //y有向圖或者帶權的有向圖
    {
        G->adj[k][j].ArcVal=arc->ArcVal;
        G->adj[k][j].ArcInfo=arc->ArcInfo;
    }
    else //無向圖或無權的無向圖
    {
        G->adj[j][k].ArcVal=G->adj[k][j].ArcVal=arc->ArcVal;
        G->adj[j][k].ArcInfo=G->adj[k][j].ArcInfo=arc->ArcInfo;
    }
    return 1;
}

這一篇介紹了基本概念和圖的鄰接矩陣的實現,下邊貼出來測試程式碼,下一篇介紹圖的鄰接連結串列的實現和圖的兩種基本遍歷方式。

#include <iostream>
#include <malloc.h>
#include "SeqGraph.h"
using namespace std;

int main()
{
    cout << "Graph:" << endl;
    MGraph *G;
    VexType vexs[4]={'A','B','C','D'};
    ArcType arc[4];
   /*
    for(int i=0;i<4;i++)
    {
        arc[i].ArcInfo=0;
        arc[i].ArcVal=i+1;
        arc[i].vex1=vex[i];
        arc[i].vex2=vex[(i+1)%4];
    }
    */
    G=(MGraph *)malloc(sizeof(MGraph));
    Create_Graph(G);
    for(int i=0;i<4;i++)
    {
        AddVex_Graph(G,&vexs[i]);
    }
    for(int i=0;i<4;i++)
    {
        arc[i].ArcInfo=0;
        arc[i].ArcVal=i+1;
        arc[i].vex1=vexs[i];
        arc[i].vex2=vexs[(i+1)%4];
        AddArc_Graph(G,&arc[i]);
    }
    for(int i=0;i<4;i++)
    {
        cout<<G->vexs[i]<<" ";
    }
    cout<<endl;
    for(int i=0;i<4;i++)
    {
        for(int j=0;j<4;j++){
            cout<<G->adj[i][j].ArcVal<<" ";
        }
        cout<<endl;
    }
    return 0;
}