1. 程式人生 > >圖的儲存結構(鄰接矩陣與鄰接表)及其C++實現

圖的儲存結構(鄰接矩陣與鄰接表)及其C++實現

一、圖的定義

圖是由頂點的有窮非空集合和頂點之間邊的集合組成,通常表示為:

                   G=(V,E)

其中:G表示一個圖,V是圖G中頂點的集合,E是圖G中頂點之間邊的集合。

注:

線上性表中,元素個數可以為零,稱為空表;

在樹中,結點個數可以為零,稱為空樹;

在圖中,頂點個數不能為零,但可以沒有邊。

二、圖的基本術語

略。

三、圖的遍歷

   圖的遍歷是在從圖中某一頂點出發,對圖中所有頂點訪問一次且僅訪問一次。

  圖的遍歷操作要解決的關鍵問題:

  ① 在圖中,如何選取遍歷的起始頂點?

  解決方案:從編號小的頂點開始 。

  線上性表中,資料元素在表中的編號就是元素在序列中的位置,因而其編號是唯一的; 在樹中,將結點按層序編號,由於樹具有層次性,因而其層序編號也是唯一的; 在圖中,任何兩個頂點之間都可能存在邊,頂點是沒有確定的先後次序的,所以,頂點的編號不唯一。 為了定義操作的方便,將圖中的頂點按任意順序排列起來,比如,按頂點的儲存順序。

  ② 從某個起點始可能到達不了所有其它頂點,怎麼辦?

  解決方案:多次呼叫從某頂點出發遍歷圖的演算法。

  ③ 因圖中可能存在迴路,某些頂點可能會被重複訪問,那麼如何避免遍歷不會因迴路而陷入死迴圈。

  解決方案:附設訪問標誌陣列visited[n] 。

  ④ 在圖中,一個頂點可以和其它多個頂點相連,當這樣的頂點訪問過後,如何選取下一個要訪問的頂點?

  解決方案:深度優先遍歷和廣度優先遍歷。

  1、深度優先遍歷

  基本思想 :

  ⑴ 訪問頂點v;

  ⑵ 從v的未被訪問的鄰接點中選取一個頂點w,從w出發進行深度優先遍歷;

  ⑶ 重複上述兩步,直至圖中所有和v有路徑相通的頂點都被訪問到。

  2、廣度優先遍歷

  基本思想:

  ⑴ 訪問頂點v;

  ⑵ 依次訪問v的各個未被訪問的鄰接點v1, v2, …, vk;

  ⑶ 分別從v1,v2,…,vk出發依次訪問它們未被訪問的鄰接點,並使“先被訪問頂點的鄰接點”先於“後被訪問頂點的鄰接點”被訪問。直至圖中所有與頂點v有路徑相通的頂點都被訪問到。

四、圖的儲存結構

    是否可以採用順序儲存結構儲存圖?

  圖的特點:頂點之間的關係是m:n,即任何兩個頂點之間都可能存在關係(邊),無法通過儲存位置表示這種任意的邏輯關係,所以,圖無法採用順序儲存結構。

  如何儲存圖?

  考慮圖的定義,圖是由頂點和邊組成的,分別考慮如何儲存頂點、如何儲存邊。

  ①鄰接矩陣(陣列表示法)

  基本思想:用一個一維陣列儲存圖中頂點的資訊,用一個二維陣列(稱為鄰接矩陣)儲存圖中各頂點之間的鄰接關係。

  假設圖G=(V,E)有n個頂點,則鄰接矩陣是一個n×n的方陣,定義為:

                    

  

  

  

  

  

  

  

  

  

  ②鄰接表

  鄰接表儲存的基本思想:對於圖的每個頂點vi,將所有鄰接於vi的頂點鏈成一個單鏈表,稱為頂點vi的邊表(對於有向圖則稱為出邊表),所有邊表的頭指標和儲存頂點資訊的一維陣列構成了頂點表。

  鄰接表有兩種結點結構:頂點表結點和邊表結點.。

                          頂點表                  邊表

  其中:vertex:資料域,存放頂點資訊。 firstedge:指標域,指向邊表中第一個結點。 adjvex:鄰接點域,邊的終點在頂點表中的下標。 next:指標域,指向邊表中的下一個結點。

  定義鄰接表的結點:

複製程式碼

// 邊表頂點
struct ArcNode
{    int adjvex; 
      ArcNode *next;
};
// 頂點表
template <class T>
struct VertexNode 
{
      T vertex;
      ArcNode *firstedge;
};

複製程式碼

五、C++程式碼實現

Ⅰ、鄰接矩陣

複製程式碼

// queue.h
#pragma once
#include <iostream>
const int queueSize = 100;
template<class T>
class queue
{
public:
    T data[queueSize];
    int front, rear;
};
// graph.h
#pragma once
#include<iostream>
#include"queue.h"
// 基於鄰接矩陣儲存結構的圖的類實現
const int MaxSize = 10;
int visited[MaxSize] = { 0 };// 頂點是否被訪問的標記
template<class T>
class MGraph
{
public:
    MGraph(T a[], int n, int e);// 建構函式建立具有N個定點e條邊的圖
    ~MGraph(){}// 解構函式
    void DFSTraaverse(int v);// 深度優先遍歷圖
    void BFSTraverse(int v);// 廣度優先遍歷圖
private:
    T vertex[MaxSize];// 存放圖中頂點的陣列
    int arc[MaxSize][MaxSize];// 存放圖中邊的陣列
    int vertexNum, arcNum;// 圖中頂點數和邊數
};

template<class T>
inline MGraph<T>::MGraph(T a[], int n, int e)
{
    vertexNum = n;
    arcNum = e;
    for (int i = 0; i < vertexNum; i++) // 頂點初始化
        vertex[i] = a[i];
    for (int i = 0; i < vertexNum; i++) // 鄰接矩陣初始化
        for (int j = 0; j < vertexNum; j++)
            arc[i][j] = 0;
    for (int k = 0; k < arcNum; k++)
    {
        int i, j;
        std::cin >> i >> j;        // 輸入邊依附的頂點的編號
        arc[i][j] = 1;            // 置有邊標記
        arc[j][i] = 1;
    }
}

template<class T>
inline void MGraph<T>::DFSTraaverse(int v)
{    
    cout << vertex[v]<<" ";
    visited[v] = 1;
    for (int j = 0; j < vertexNum; j++)
    {
        if (arc[v][j] == 1 && visited[j] == 0)
            DFSTraaverse(j);
    }
}

template<class T>
inline void MGraph<T>::BFSTraverse(int v)
{
    int visited[MaxSize] = { 0 };// 頂點是否被訪問的標記
    queue<T> Q;
    Q.front = Q.rear = -1;    // 初始化佇列
    cout << vertex[v]<<" ";
    visited[v] = 1;
    Q.data[++Q.rear] = v;    // 被訪問頂點入隊
    while (Q.front != Q.rear)
    {
        v = Q.data[++Q.front];    // 對頭元素出隊
        for (int j = 0; j < vertexNum; j++)
        {
            if (arc[v][j] == 1 && visited[j] == 0)
            {
                std::cout << vertex[j]<<" ";
                visited[j] = 1;
                Q.data[++Q.rear] = j;    // 鄰接點入隊
            }
        }
    }
}
// main.cpp
#include"graph.h"
using namespace std;
int main()
{
    int arry[] = { 1,2,3,4,5,6 };
    MGraph<int> graph(arry, 6, 9);
    graph.BFSTraverse(1);
    cout << endl;
    graph.DFSTraaverse(1);
    system("pause");
    return 0;
}

複製程式碼

 Ⅱ、鄰接表

複製程式碼

// queue.h
#pragma once
#include <iostream>
const int queueSize = 100;
template<class T>
class queue
{
public:
    T data[queueSize];
    int front, rear;
};
// graph.h
#pragma once
#include<iostream>
#include"queue.h"
// 定義邊表結點
struct ArcNode
{
    int adjvex;// 鄰接點域
    ArcNode* next;
};
// 定義頂點表結點
struct VertexNode
{
    int vertex;
    ArcNode* firstedge;
};

// 基於鄰接表儲存結構的圖的類實現
const int MaxSize = 10;
int visited[MaxSize] = { 0 };// 頂點是否被訪問的標記
//typedef VertexNode AdjList[MaxSize];    //鄰接表 
template<class T>
class ALGraph
{
public:
    ALGraph(T a[], int n, int e);// 建構函式建立具有N個定點e條邊的圖
    ~ALGraph() {}// 解構函式
    void DFSTraaverse(int v);// 深度優先遍歷圖
    void BFSTraverse(int v);// 廣度優先遍歷圖
private:
    VertexNode adjlist[MaxSize];// 存放頂點的陣列
    int vertexNum, arcNum;// 圖中頂點數和邊數
};

template<class T>
ALGraph<T>::ALGraph(T a[], int n, int e)
{
    vertexNum = n;
    arcNum = e;
    for (int i = 0; i <vertexNum; i++)
    {
        adjlist[i].vertex = a[i];
        adjlist[i].firstedge = NULL;
    }
    for (int k = 0; k < arcNum; k++)
    {
        int i, j;
        std::cin >> i >> j;
        ArcNode* s = new ArcNode;
        s->adjvex = j;
        s->next = adjlist[i].firstedge;
        adjlist[i].firstedge = s;
    }
}

template<class T>
inline void ALGraph<T>::DFSTraaverse(int v)
{
    std::cout << adjlist[v].vertex;
    visited[v] = 1;
    ArcNode* p = adjlist[v].firstedge;
    while (p != NULL)
    {
        int j = p->adjvex;
        if (visited[j] == 0)
            DFSTraaverse(j);
        p = p->next;
    }
}

template<class T>
inline void ALGraph<T>::BFSTraverse(int v)
{
    int visited[MaxSize] = { 0 };// 頂點是否被訪問的標記
    queue<T> Q;
    Q.front = Q.rear = -1;    // 初始化佇列
    std::cout << adjlist[v].vertex;
    visited[v] = 1;
    Q.data[++Q.rear] = v;// 被訪問頂點入隊
    while (Q.front != Q.rear)
    {
        v = Q.data[++Q.front];    // 對頭元素出隊
        ArcNode* p = adjlist[v].firstedge;
        while (p != NULL)
        {
            int j = p->adjvex;
            if (visited[j] == 0)
            {
                std::cout << adjlist[j].vertex;
                visited[j] = 1;
                Q.data[++Q.rear] = j;
            }
            p = p->next;
        }
    }
}
// main.cpp
#include"graph.h"
using namespace std;
int main()
{
    int arry[] = { 1,2,3,4,5 };
    ALGraph<int> graph(arry, 5, 7);
    graph.BFSTraverse(3);
    cout << endl;
    graph.DFSTraaverse(3);
    system("pause");
    return 0;
}