1. 程式人生 > >用鄰接連結串列儲存無向圖和有向圖

用鄰接連結串列儲存無向圖和有向圖

上一篇文章我們說到用鄰接矩陣儲存有向圖和無向圖,這種方法的有點是很直觀的知道點與點之間的關係,查詢的複雜度是為O(1)。但是這種儲存方式同時存在著佔用較多儲存空間的問題,

鄰接矩陣儲存很好理解,但是,有時候太浪費空間了,特別是對於頂點數多,但是關聯關係少的圖。

舉個極端的例子。

下圖中,5個頂點都是孤立的,然而為了儲存邊的資訊,要分配一個5X5的矩陣。本來一條邊都沒有,沒必要用儲存空間來儲存邊,但還要分配額外的空間。

            

用鄰接表則可以避免這種浪費。鄰接表用動態表結構儲存邊,更加靈活:有一個邊就分配一個空間去儲存,沒有就不分配。

我們仍然用鄰接矩陣中那個圖的例子來說明鄰接表的構建思想。

鄰接表只需要一個數組,但是這個陣列有點複雜,需要解剖一下。如下圖。

 可以發現,用鄰接表儲存時,圖中的每一個節點 不僅要儲存本頂點的資料data,還要儲存一個表,這個表儲存了此頂點的所有臨接點的索引。2點確定一線,那麼就能儲存此節點相關的所有的邊了。

下面我們用到C++庫中的vector來實現indexlist。

程式碼如下:

#include<stdio.h>
#include<vector>

#define MAX_VERTEX 4
using std::vector;        //使用C++標準庫中的vector來作為list,它實質就是一個順序表 


typedef char DataType;

typedef struct node
{
    DataType data;            //頂點資料 
    vector<int> indexList;    //儲存頂點鄰接點索引的 表 

}Node;


typedef Node* UListGraph;


/********************************/
void UListGraph_init(UListGraph*pGraph);
void UListGraph_create(UListGraph graph);
void UListGraph_show(UListGraph graph);
void UListGraph_destroy(UListGraph*pGraph);

int main(void)
{

    UListGraph g;

    UListGraph_init(&g);
    UListGraph_create(g);
    UListGraph_show(g);

    UListGraph_destroy(&g);



    return 0;
}


void UListGraph_init(UListGraph*pGraph)
{

    (*pGraph) = new Node[MAX_VERTEX];


}


void UListGraph_create(UListGraph  graph)
{

    
    
    for (int i = 0; i < MAX_VERTEX; ++i)  //填充頂點陣列
    {
        printf("輸入第%d個頂點值\n",i+1);
        
        scanf(" %c",  &(graph[i].data)); 

    }

    for(int i=0;i<MAX_VERTEX;++i)
        for(int j=i+1;j<MAX_VERTEX;++j)
        {
            printf("如果元素%c和%c之間有邊,請輸入1,否則輸入0",graph[i].data,graph[j].data);

            int opt;
            scanf("%d",&opt);

            if(opt==1)
            {
                graph[i].indexList.push_back(j);
                graph[j].indexList.push_back(i);
            }


        }



}



void UListGraph_show(UListGraph graph)
{
    
    printf("頂點元素如下:\n\n"); 
    for(int i=0;i<MAX_VERTEX;i++)
    {
        printf("%c\t",graph[i].data);
    }
    printf("\n\n");



    for(int i=0;i<MAX_VERTEX;i++)
    {
        printf("元素%c的鄰接點 :",graph[i].data);

        Node tnode = graph[i];

        for(int k=0;k<tnode.indexList.size();++k)
        {
            printf("%c\t",graph[tnode.indexList[k]].data);
        }

        putchar('\n');
    }


}


void UListGraph_destroy(UListGraph*pGraph)
{
    delete (*pGraph);
    *pGraph = NULL;        
       

}
2、有向網用鄰接連結串列儲存

鄰接表對於無向圖來說很適用,但是對於有向圖來說就不適用了,因為鄰接表中,每一個節點的IndexList只能儲存一種狀態的弧,出弧或者入弧(逆鄰接表),那怎麼將一個頂點的出入弧都儲存起來呢?

那就是將鄰接表和逆鄰接表都用起來,也就是一個節點需要儲存:①data,②inArcList,入弧記錄表,③outArcList,出弧記錄表


實現程式碼如下:

#include<stdio.h>
#include<vector>

using std::vector;

#define MAX_VERTEX 4 

typedef char DataType;


typedef struct arc{ 

    int headVertex;         //此條弧的頭尾結點的index
    int tailVertex;         //此條弧的頭尾結點的index

    //WeightType weight;    //此弧的附加資訊,比如權重,這裡是有向圖,不是網,所以不需要weight

}Arc;

typedef struct node{

    DataType data;   //頂點資料域

    vector<Arc> outArc;     //此頂點的所有 出度 弧 表
    vector<Arc> inArc;      //此頂點的所有 入度 弧 表 

}Node;


typedef Node* Graph;

void Graph_init(Graph* pG);
void Graph_create(Graph g);
void Graph_show(Graph g);
void Graph_destroy(Graph*pg);


int main(void)
{


    Graph g;

    Graph_init(&g);
    Graph_create(g);
    Graph_show(g);

    return 0;
}



void Graph_init(Graph* pG)
{

    (*pG) = new Node[MAX_VERTEX];
    if(*pG == NULL)
    {
        //exit(-1);
    }

}

void Graph_create(Graph g)
{

    int opt;
    
    for (int i = 0; i < MAX_VERTEX; ++i)  //填充頂點陣列
    {
        printf("輸入第%d個頂點值\n",i+1);
        
        scanf(" %c",  &(g[i].data)); 

    }

    for(int j=0;j<MAX_VERTEX;++j)
        for(int i=j+1;i<MAX_VERTEX;++i)
        {


            printf("若元素%c有指向%c的弧,則輸入1,否則輸入0\t",g[j].data,g[i].data);
           
            scanf("%d",&opt);

            if(opt==1)
            {
                Arc* parc = new Arc;
                parc->headVertex = i;
                parc->tailVertex = j;
                
                g[j].outArc.push_back(*parc);
                g[i].inArc.push_back(*parc);

            }

            
            printf("若元素%c有指向%c的弧,則輸入1,否則輸入0\t",g[i].data,g[j].data);
           
            scanf("%d",&opt);

            if(opt==1)
            {
                
                Arc* parc = new Arc;
                parc->headVertex = j;
                parc->tailVertex = i;

                g[i].outArc.push_back(*parc);
                g[j].inArc.push_back(*parc);
                     

            }

        }

}

void Graph_show(Graph g)
{


    for (int j = 0; j <MAX_VERTEX; ++j)
    {
        printf("頂點資料%c\n",g[j].data);
       
        printf("發射:");

        
        for(int i=0;i<g[j].outArc.size();++i)
        {
            printf("%c\t",g[g[j].outArc[i].headVertex].data);
        }


        putchar('\n');
        printf("接收:");

        for(int i=0;i<g[j].inArc.size();++i)
        {
            printf("%c\t",g[g[j].inArc[i].tailVertex].data);
        }

        printf("\n\n");

    }
    


}



void Graph_destroy(Graph*pg)
{
  delete (*pg);
    *pg = NULL;        
       
}