c++鄰接表存儲圖(無向),並用廣度優先和深度優先遍歷(實驗)


一開始我是用c寫的,后面才發現廣搜要用到隊列,所以我就直接使用c++的STL隊列來寫,

因為不想再寫多一個隊列了。這次實驗寫了兩個多鍾,因為要邊寫邊思考,太菜了哈哈。

主要參考《大話數據結構》這本書,然后加上自己的一些東西改編,這次實驗算是完成了;

--------------------------------------------------------------------------------

首先我們來看一下鄰接表是怎么存儲圖的,比如說下面有一個無向圖

則它的鄰接表是這樣的,鄰接表有兩個部分,一個是頂點表,一個是邊表。頂點表長這樣

然后就看,它有好多個頂點,所以,我們給它開個數組,長這樣

 那個下標是輸入頂點的順序,就是一個位置

上面這一坨就是鄰接表的頂點表,顧名思義,就是用來存它的頂點的一個表

然后接下來是邊表,邊表是用來干什么的呢?邊表就是用來存一個頂點的鄰接點,

如剛才的無向圖,

頂點v0的鄰接點就是v1,v3了,v2的鄰接點就是v1,v3,v4

所以由此我們可得出邊表的樣子,長這樣

邊表就是這樣子,用來存頂點的鄰接點的,每一行都是一個邊表,所以一個頂點對應一個邊表

那些箭頭就是指針,指向下一個結點,然后null表示為空

現在頂點表有了,邊表也有了,我們把它合並起來就很清楚,look;

這個就是一個圖的鄰接表了,如果是有向圖,則在adjvex和next中間加多一個weight用來存儲權值就行了,但在這我就不再講了

好了,圖出來了,看看代碼怎么實現;

首先,我們得定義好頂點表和邊表的結構,因為它們都是一種新的數據類型

代碼如下:

 

//邊表節點結構,一個adjvex用來存儲鄰接點的位置,一個next指針用來指向下一個節點
typedef struct EdgeNode
{
    int adjvex;
    struct EdgeNode * next;
} EdgeNode;

//頂點表節點結構,一個data用來存儲數據,一個firstedge是用來指向邊表的第一個節點
typedef struct
{
    string data;
    EdgeNode * firstedge;
} AdjList;

 

然后我們就可以定義一個圖的鄰接表了,這樣

//里面的adjList[15]表示我給頂點表開了15的單位大小,然后numVertex,numEdge是一個圖的頂點數和邊數
typedef struct
{
    AdjList adjList[15];
    int numVertex,numEdge;
} GraphAdjList;

 所以后面我們想定義一個新的圖的鄰接表,可以直接 GraphAdjList G 就行了;

 

重點來了,定義圖的鄰接表我們搞定了,接下來就是創建了

在此之前我們得先看看一個東西

那就是寫一個函數,用來返回一個頂點所在的位置,這個會有用到

代碼如下:

 

//這個函數是這樣的,它會遍歷圖的頂點,然后返回一個位置(其實也就是它所在的下標)
int local(GraphAdjList G,string val)
{
    for(int i=0; i<G.numVertex; i++)
    {
        if(G.adjList[i].data==val)
            return i;
    }
    return -1;
}
//比如v2的位置是在2 這個可以看上面的頂點表圖

 

 

 

 接下來就是創建一個圖的鄰接表,我會邊用代碼邊用圖來表示,這樣就可以更好的理解代碼了

創建圖的代碼如下

void CreateGraph(GraphAdjList & G)
{
    int i,j,k;
    string v1,v2;
    EdgeNode * e,* p,*q;
    cout<<"請輸入頂點數和邊數,並以空格隔開:"<<endl;
    cin>>G.numVertex>>G.numEdge;
    cout<<"請輸入頂點的信息:"<<endl;
    for(i=0; i<(G.numVertex); i++)
    {
        cout<<"第"<<i+1<<"個頂點:"<<endl;
        cin>>G.adjList[i].data;
        G.adjList[i].firstedge=NULL;
    }
    for(k=0; k<(G.numEdge); k++)
    {
        cout<<"請輸入邊(Vi,Vj)上的頂點信息:"<<endl;
        cin>>v1>>v2;
        i=local(G,v1);
        j=local(G,v2);

        if(G.adjList[i].firstedge==NULL)
        {
            e= new EdgeNode;
            e->adjvex=j;
            e->next=NULL;
            G.adjList[i].firstedge=e;
        }
        else
        {
            p=G.adjList[i].firstedge;
            while(p->next!=NULL)
            {
                p=p->next;
            }
            e = new EdgeNode;
            e->adjvex=j;
            e->next=NULL;
            p->next=e;
        }
        if(G.adjList[j].firstedge==NULL)
        {
            e= new EdgeNode;
            e->adjvex=i;
            e->next=NULL;
            G.adjList[j].firstedge=e;
        }
        else
        {
            p=G.adjList[j].firstedge;
            while(p->next!=NULL)
            {
                p=p->next;
            }
            e = new EdgeNode;
            e->adjvex=i;
            e->next=NULL;
            p->next=e;
        }
    }
}

  

接下來我們拆分一些主要代碼,看看是怎么實現的;

cin>>G.adjList[i].data;

G.adjList[i].firstedge=NULL;

這里是往頂點表插入頂點,並把firstedge域置空,如圖:

cin>>v1>>v2;

i=local(G,v1);

j=local(G,v2); 

輸入每條邊的兩端的頂點,一般我們是從小到大輸入的,例如這里我們

先是輸入v0 v1  ,  v0  v3這樣的,然后它會返回位置分別是0 1,0 3

 

然后后面邊表的插入方法時,我用的是尾插法,因為這樣才能達到跟圖片一樣的效果

不然用頭插法的話,那個邊表的順序是反的;

當我們執行完這個CreateGraph函數的時候,就已經創建了如下圖所示的鄰接表了;

 

創建完成后,我們就可以把鄰接表輸出了,而且是按照圖中所示一樣的輸出

代碼如下:

void Prin(GraphAdjList G)
{
    cout<<"所建立的鄰接表如以下所示:"<<endl;
    for(int i=0; i<G.numVertex; i++)
    {
        cout<<G.adjList[i].data;             //先輸出頂點信息
        EdgeNode * e = G.adjList[i].firstedge;
        while(e)                              //然后就開始遍歷輸出每個邊表所存儲的鄰接點的下標
        {
            cout<<"-->"<<e->adjvex;
            e=e->next;
        }
        cout<<endl;
    }
}

  

 

至此,我們創建一個圖的鄰接表並可以輸出該鄰接表了,接下來我們

就是廣度優先遍歷和深度優先遍歷了

關於這兩個遍歷的定義我也不多講了,你們可以查資料,我直接就貼

上代碼就好了

①深度優先遍歷

這個遍歷的重點就是它的算法

void DFS(GraphAdjList  G,int i)
{

    EdgeNode * p;
    DFSvisited[i]=true;
    cout<<G.adjList[i].data<<"  ";
    p=G.adjList[i].firstedge;
    while(p)
    {
        if(!DFSvisited[p->adjvex])
            DFS(G,p->adjvex);
        p=p->next;
    }
}

 

 

然后就是使用這個算法了

void DFSTraverse(GraphAdjList  G)
{
    for(int i=0; i<G.numVertex; i++)
        DFSvisited[i]=false;
    for(int i=0; i<G.numVertex; i++)
    {
        if(!DFSvisited[i])
            DFS(G,i);
    }
}

  

所以這兩部分代碼結合起來就是深度優先遍歷了

對了,那個DFSvisited[i]是一個bool型的數組,

用來標記是否遍歷過的,可以在前面加上

bool DFSvisited[50];  //用於深搜的標記數組
bool BFSvisited[50];  //用於廣搜的標記數組

  

②廣度優先遍歷

這個遍歷直接一個函數就能搞定,需要用到隊列,

相信你們既然在看圖的部分的話,那么隊列肯定

也早已學會了,嘿嘿。

void BFSTraverse(GraphAdjList  G)
{
    EdgeNode * p;
    queue<int>q;
    for(int i=0; i<G.numVertex; i++)
        BFSvisited[i]=false;
    for(int i=0; i<G.numVertex; i++)
    {
        if(!BFSvisited[i])
        {
            BFSvisited[i]=true;
            cout<<G.adjList[i].data<<"  ";
            q.push(i);
            while(!q.empty())
            {
                int count =q.front();
                q.pop();
                p=G.adjList[count].firstedge;
                while(p)
                {
                    if(!BFSvisited[p->adjvex])
                    {
                        BFSvisited[p->adjvex]=true;
                        cout<<G.adjList[p->adjvex].data<<"  ";
                        q.push(p->adjvex);
                    }
                    p=p->next;
                }
            }
        }
    }
}

  

啊咧咧,還有一個銷毀圖的操作

void DestoryGraph(GraphAdjList & G)
{
    EdgeNode * p = NULL;
    for(int i=0; i<G.numVertex; i++)
    {
        p=G.adjList[i].firstedge;
        while(p)
        {
            EdgeNode * temp = p;
            p=p->next;
            delete temp;
        }
        G.adjList[i].firstedge=NULL;
    }
}

  

至此,整篇文章可以結束了,有什么錯誤或者問題可在下方留言,

一起探討

貼上測試效果圖:

 

 你們可以注意一下鄰接表的輸出跟我上面給的圖是一樣的

拜拜,我去做大保健了。

 


免責聲明!

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



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