圖的存儲結構(鄰接矩陣與鄰接表)及其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;
}

 

參考文獻:

[1]王紅梅, 胡明, 王濤. 數據結構(C++版)[M]. 北京:清華大學出版社。

 2018-01-07

 


免責聲明!

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



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