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

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

由於不想再寫多一個隊列了。此次實驗寫了兩個多鍾,由於要邊寫邊思考,太菜了哈哈。算法

主要參考《大話數據結構》這本書,而後加上本身的一些東西改編,此次實驗算是完成了;數組

--------------------------------------------------------------------------------數據結構

首先咱們來看一下鄰接表是怎麼存儲圖的,好比說下面有一個無向圖函數

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

而後就看,它有好多個頂點,因此,咱們給它開個數組,長這樣spa

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

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

而後接下來是邊表,邊表是用來幹什麼的呢?邊表就是用來存一個頂點的鄰接點,blog

如剛纔的無向圖,

頂點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;
    }
}

  

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

一塊兒探討

貼上測試效果圖:

 

 大家能夠注意一下鄰接表的輸出跟我上面給的圖是同樣的

拜拜,我去作大保健了。

相關文章
相關標籤/搜索