數據結構之圖的鄰接表


1.鄰接表的簡介:

圖的鄰接矩陣存儲方法跟樹的孩子鏈表示法相類似,是一種順序分配和鏈式分配相結合的存儲結構。鄰接表由表頭結點和表結點兩部分組成,其中圖中每個頂點均對應一個存儲在數組中的表頭結點。如這個表頭結點所對應的頂點存在相鄰頂點,則把相鄰頂點依次存放於表頭結點所指向的單向鏈表中。如詞條概念圖所示,表結點存放的是鄰接頂點在數組中的索引。對於無向圖來說,使用鄰接表進行存儲也會出現數據冗余,表頭結點A所指鏈表中存在一個指向C的表結點的同時,表頭結點C所指鏈表也會存在一個指向A的表結點。[1] 
鄰接表是圖的一種最主要存儲結構,用來描述圖上的每一個點。對圖的每個頂點建立一個容器(n個頂點建立n個容器),第i個容器中的結點包含頂點Vi的所有鄰接頂點。實際上我們常用的鄰接矩陣就是一種未離散化每個點的邊集的鄰接表。
有向圖中,描述每個點向別的節點連的邊(點a->點b這種情況)。
在無向圖中,描述每個點所有的邊(點a-點b這種情況)
與鄰接表相對應的存圖方式叫做邊集表,這種方法用一個容器存儲所有的邊。
工業上有很多非常好的圖庫的實現,例如C++的boost graph庫.如果可以,盡量用這些庫,這樣可以大大提高你的效率
2.表示法
注意:
n個頂點e條邊的無向圖的鄰接表表示中有n個頂點表結點和2e個邊結點。(換句話說,每條邊(i,j)在鄰接表 中出現兩次:一次在關於i的鄰接表中,另一次在關於j的鄰接表中)

有向圖

對於有向圖,vi的鄰接表中每個表結點都對應於以vi為始點射出的一條邊。因此,將有向圖的鄰接表稱為出邊表。
【例】有向圖G6如下圖所示,其中頂點v1的鄰接表上兩個表結點中的頂點序號分別為0和4,它們分別表示從v1射出的兩條邊(簡稱為v1的出邊):<v1,v0>和<v1,v4>。
注意:
n個頂點e條邊的有向圖,它的鄰接表表示中有n個頂點表結點和e個邊表結點。(因為有向圖是單向的)

逆鄰接表

在有向圖中,為圖中每個頂點vi建立一個入邊表的方法稱逆鄰接表表示法。
入邊表中的每個表結點均對應一條以vi為終點(即射入vi)的邊。
【例】G6的逆鄰表如上面(b)圖所示,其中v0的入邊表上兩個表結點1和3分別表示射人v0的兩條邊(簡稱為v0的入邊):<v1,v0>和<v3,v0>。
注意:
n個頂點e條邊的有向圖,它的逆鄰接表表示中有n個頂點表結點和e個邊表結點。
3.鄰接表的形式說明。
鄰接表是一個二維容器,第一維描述某個點,第二維描述這個點所對應的邊集們。
實現鄰接表的方法絕對有100種以上。即使是前向星這種東西也是鄰接表,因為它還是描述某個點和這個點所對應的邊集們.
我們說說常用的鄰接表存圖法(靜態的array就不說了.)必須有開O1以及以上編譯的條件,不然沒有測試的效率無任何意義。
第一維是描述點的。可以用vector,list,forward_list,deque,map,multimap,unordered_map,unordered_multimap等(一般不能用set,mutiset,unordered_set,unordered_multiset).
按照你的要求去選擇。一般來講存完圖以后不涉及點的加入與刪除優先使用vector.map,multimap,unordered_map,unordered_multimap.
第二維是描述這個點的邊集,可以用全部的容器。 也是的 一般來講存完圖以后,不涉及點的加入與刪除優先使用vector,空間充足可以考慮deque.涉及點的刪除用forward_list或者是list,map,multimap,unordered_map,unordered_multimap.
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <queue>
using namespace std;
#define N 200
typedef enum GraphKind {DG,DN,UDG,UDN};
typedef char VertexType[20];
typedef struct ArcNode
{
    int adjvex;//該弧指向的頂點的位置
    int *info;   //該弧的相關信息
    ArcNode *nextarc;//指向下一條弧的指針
};
typedef struct
{
    VertexType data;//頂點信息
    ArcNode *firstarc; //指向第一條依附該頂點的弧的指針
} VNode,AdjList[N];
typedef struct ALGraph
{
    AdjList vertices;
    int vexnum,arcnum;//弧的當前頂點數和弧數
    int kind;//圖的種類
};
//1.頂點的位置:
int LocateVex(ALGraph g,VertexType u)
{
    int i;
    for(i=0; i<g.vexnum; i++)
    {
        if(strcmp(g.vertices[i].data,u)==0) return i;
    }
    return -1;
}
//2.采用鄰接表存儲結構,構造沒有相關信息的圖G
//(用一個函數構造4種圖)
void CreateGraph(ALGraph &G)
{
    int i, j, k;
    int w;      //權值
    VertexType va , vb ;
    ArcNode *p;
    cout << "請輸入圖的類型(有向圖:0,有向網:1,無向圖:2,無向網:3):" << endl;
    cin >> G.kind;
    cout << "請輸入圖的頂點數,邊數: " << endl;
    cin >> G.vexnum >> G.arcnum;
    cout  << "請輸入" << G.vexnum << "個頂點的值(" << "1" << "個字符)每個用空格隔開:" << endl;
    for( i = 0; i <G.vexnum; i++ )      //構造頂點向量
    {
        cin >> G.vertices[i].data;
        G.vertices[i].firstarc = NULL;
    }
    if( G.kind == 1 || G.kind == 3 )    //
        cout << "請順序輸入每條弧(邊)的權值,弧尾和弧頭(以空格作為間隔):" << endl;
    else                                //
        cout << "請順序輸入每條弧邊的弧尾和弧頭(以空格作為間隔):" << endl;
    for( k = 0; k < G.arcnum; k++ )     //構造表結點鏈表
    {
        if( G.kind == 1 || G.kind == 3 )  //
            cin >> w >> va >> vb;
        else                             //
            cin >> va >> vb;
        i = LocateVex( G, va );          //弧頭
        j = LocateVex( G, vb );          //弧尾
        p = ( ArcNode * )malloc( sizeof( ArcNode ) );
        p->adjvex = j;
        if( G.kind == 1 || G.kind == 3 )
        {
            p->info = ( int * )malloc( sizeof( int ) );
            *( p->info ) = w;
        }
        else
            p->info = NULL;
        p->nextarc = G.vertices[i].firstarc;  //插在表頭
        G.vertices[i].firstarc = p;
        if( G.kind >= 2 )                        //無向圖或網,產生第二個表結點
        {
            p = ( ArcNode* )malloc( sizeof( ArcNode ) );
            p->adjvex = i;
            if( G.kind == 3 )               //無向圖
            {
                p->info = ( int * )malloc( sizeof( int ) );
                *( p->info ) = w;
            }
            else
                p->info = NULL;            //無向圖
            p->nextarc = G.vertices[j].firstarc;  //插在表頭
            G.vertices[j].firstarc = p;
        }
    }
}
void Prt(ALGraph G)
{
    int i;
    printf("vexnum :%d, arcnum :%d.\n\n",G.vexnum,G.arcnum);
    printf("VexList:\n");
    for(i = 0; i < G.vexnum; i++){
        printf("        v(%d):%s|",i,G.vertices[i].data);
        ArcNode *p = G.vertices[i].firstarc;
        while(p){
            printf("->%d",p->adjvex);
            p = p->nextarc;
        }
        printf("\n");
    }
}
void find(ALGraph &g,VertexType a,VertexType b)
{
    int i,j;
    i=LocateVex(g,a);
    j=LocateVex(g,b);
    ArcNode *p,*q1,*q2;
    if(g.kind>=2)
    {
        p=g.vertices[i].firstarc;
        int flag=0;
        while(p)
        {
            if(p->adjvex==j)
            {
                flag=1;
                break;
            }
            p=p->nextarc;
        }
        if(flag==1)
        {
            cout<<"YES"<<endl;
            if(g.kind%2==1) cout<<*(p->info)<<endl;
        }
        else
        {
            cout<<"NO"<<endl;
        }
    }
    else
    {
        q1=g.vertices[i].firstarc;
        int flag1=0,flag2=0;
        while(q1)
        {
            if(q1->adjvex==j)
            {
                flag1=1;
                break;
            }
            q1=q1->nextarc;
        }
        q2=g.vertices[j].firstarc;
        while(q2)
        {

            if(q2->adjvex==i)
            {
                flag2=1;
                break;
            }
            q2=q2->nextarc;
        }
        if(flag1==1)
        {
            cout<<a<<"->"<<b<<endl;
            if(g.kind%2==1) cout<<*(q1->info)<<endl;
        }
        else if(flag2==1)
        {
            cout<<b<<"->"<<a<<endl;
            if(g.kind%2==1) cout<<*(q2->info)<<endl;
        }
        else
        {
            cout<<"NO"<<endl;
        }
    }
}
void degree(ALGraph &g,VertexType a)
{
    int i,j,k;
    ArcNode *p,*q;
    if(g.kind>1)
    {
        int sum=0;
        i=LocateVex(g,a);
        p=g.vertices[i].firstarc;
        while(p)
        {
            sum++;
            p=p->nextarc;
        }
        cout<<a<<"的度為:"<<sum<<endl;
    }
    else
    {
        int sum1=0,sum2=0;
        i=LocateVex(g,a);
        p=g.vertices[i].firstarc;
        while(p)
        {
            sum1++;
            p=p->nextarc;
        }
        for(j=0; j<g.vexnum; j++)
        {
            if(j!=i)
            {
                q=g.vertices[j].firstarc;
                while(q)
                {
                    if(q->adjvex==i)
                    {
                        sum2++;
                    }
                    q=q->nextarc;
                }
            }
        }
        cout<<a<<"的度為:"<<(sum1+sum2)<<endl;
    }
}
bool vis[N];
//返回v的第一個鄰接頂點的序號,若圖中無鄰接頂點,返回-1
int FirstAdjVex(ALGraph G,VertexType v){
    int i = LocateVex(G,v);
    ArcNode *p = G.vertices[i].firstarc;
    if(p){
        return p->adjvex;
    }
    else{
        return -1;
    }
}
//返回v的(相對於w的)下一個鄰接頂點的序號。若w是v的最后一個鄰接頂點,則返回-1
int NextAdjVex(ALGraph G,VertexType v,VertexType w){
    int i = LocateVex(G,v);
    int j = LocateVex(G,w);
    ArcNode *p = G.vertices[i].firstarc;
    while(p){
        if(p->adjvex == j) break;
        p = p->nextarc;
    }
    if(!p || !p->nextarc){ //沒找到w或w是最后一個鄰接點
                          return -1;
                         }
    else{
        return (p->nextarc)->adjvex;
    }
}
//深度優先搜索
void DFS(ALGraph G,int v){
    int w;
    vis[v] = true;
    printf("%s ",G.vertices[v].data); //訪問第v個頂點

    w = FirstAdjVex(G,G.vertices[v].data);
    while(w >= 0){
        if(!vis[w]){
            DFS(G,w);
        }
        w = NextAdjVex(G,G.vertices[v].data,G.vertices[w].data);
    }
}
//對圖做深度優先遍歷
void DFSTraverse(ALGraph G){
    int v;
    memset(vis,false,sizeof(vis));
    for(v = 0; v < G.vexnum; v++){
        if(!vis[v]){
            DFS(G,v);//對尚未訪問的頂點調用DFS
        }
    }
    printf("\n");
}
//對圖做廣度優先搜索
void BFSTraverse(ALGraph G)
{
    int v,u,w;
    queue<int>q;
    memset(vis,false,sizeof(vis));
    for(v = 0; v < G.vexnum; v++){
        //如果是連通圖,v = 0就遍歷全圖
        if(!vis[v]){
            vis[v] = true;
            printf("%s ",G.vertices[v].data);
        }
        q.push(v);
        while(!q.empty()){
            u=q.front();
            q.pop(); //隊頭元素出隊,並置為u
            w = FirstAdjVex(G,G.vertices[u].data);
            while(w >= 0){
                if(!vis[w]){
                    vis[w] = true;
                    printf("%s ",G.vertices[w].data);
                   q.push(w);//入隊
                }
                w = NextAdjVex(G,G.vertices[u].data,G.vertices[w].data);
            }
        }
    }
    printf("\n");
}
int main()
{
    ALGraph g;
    CreateGraph(g);
    printf("輸出鄰接表:\n");
    Prt(g);
    VertexType a,b;
    printf("輸入兩個頂點:\n");
    cin>>a>>b;
    printf("輸出頂點之間是否存在邊:\n");
    find(g,a,b);
    printf("輸入一個頂點:\n");
    cin>>a;
    printf("輸出該點的度:\n");
    degree(g,a);
    printf("深度優先搜索遍歷:\n");
    DFSTraverse(g);
    printf("廣度優先搜索遍歷:\n");
    BFSTraverse(g);
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM