淺析數據結構-圖的基本概念


線性表和樹兩類數據結構,線性表中的元素是“一對一”的關系,樹中的元素是“一對多”的關系,本章所述的圖結構中的元素則是“多對多”的關系。圖(Graph)是一種復雜的非線性結構,在圖結構中,每個元素都可以有零個或多個前驅,也可以有零個或多個后繼,也就是說,元素之間的關系是任意的。

一、圖的定義與術語

定義:圖(Graph)是由頂點的有窮非空集合和頂點之間邊的集合組成,通常表示為:G(V,E),其中,G表示一個圖,V是圖G中頂點的集合,E是圖G中邊的集合。

1、圖的分類

圖是按照無方向和有方向分為無向圖和有向圖。

clip_image001

左圖為無向圖是由頂點和邊構成,右圖為有向圖是由頂點和弧(有向邊構成)。弧有弧頭和弧尾區別。

按照邊分為稀疏圖和稠密圖,這是個模糊的概念,同樣是相對的概念。

如果任意兩個頂點之間都存在邊叫完全圖,有向的邊叫有向完全圖。如果無重復的邊或者頂點到自身的邊叫簡單圖。在用數學方式表示時,無向邊用()表示,有向邊用<>表示。現在我們講解的圖全是簡單圖。

clip_image002

左圖沒有重復的邊或者到自身的邊(簡單圖),右圖則有。

clip_image005

這種邊帶權值的圖叫網

2.圖的頂點和邊間關系

頂點的度:頂點關聯邊的數目。有向圖圖中有,入度:方向指向頂點的邊;出度:方向背向頂點的邊。在有向圖中頂點的度就是兩者之和。

路徑長度:路徑上邊或者弧的數目。

clip_image006

左圖中,從B到D的路徑度為2,在右圖中就是3了(粗線的邊)。

右圖中A的入度是2,出度是1;B的入度為0,出度是2.

連通

在無向圖G中,任意兩個頂點是相通的就是連通圖。

clip_image007

左圖不是連通圖,AE之間沒有連通。

二、圖的存儲結構

圖的結構比價復雜,任意兩個頂點之間都可能存在關系,不能用簡單的順序存儲結構來表示。如果運用多重鏈表,即一個數據域多個指針域組成的結點表示圖中一個結點,則造成大量存儲單元浪費。

1、鄰接矩陣

鄰接矩陣用兩個數組保存數據。一個一維數組存儲圖中頂點信息,一個二維數組存儲圖中邊或弧的信息。

clip_image009

無向圖中二維數組是個對稱矩陣。

特點:

  • 1、0表示無邊,1表示有邊

        2、頂點的度是行內數組之和。

        3、求取頂點鄰接點,將行內元素遍歷下。

    clip_image010

    有向圖的鄰接矩陣:有向圖中講究入度和出度,各行之和是出度,各列之和是入度。

    帶權的圖叫網,用鄰接矩陣表示為:

    clip_image012

    鄰接矩陣對於邊數相對頂點較少的圖,就是對存儲空間極大的浪費。

    2、鄰接表

    鄰接表:數組和鏈表相結合的存儲方法為鄰接表。

  • 圖中頂點用一個一維數組存儲。
  • 圖中每個頂點Vi的所有鄰接點構成一個線性表。

    clip_image013

    從圖中得知,頂點表的各個結點由data和Firstedge兩個域表示,data是數據域,存儲頂點信息,firstedge是指針域,指向邊表的第一個結點,即頂點的第一個鄰接點。邊表結點由adjvex和next兩個域組成。adjvex是鄰接點域,存儲某頂點的鄰接點在頂點表中坐標,next存儲邊表中下一個結點指針。比如v1頂點與v2、v0互為鄰接點,則在v1邊表中,adjvex分別為0和2。

    有向圖也可以用鄰接表,出度表叫鄰接表,入度表尾逆鄰接表。

    clip_image014

    3、十字鏈表

    在鄰接表中針對有向圖,分為鄰接表和逆鄰接表,導致無法從一個表中獲取圖的入讀和出度的情況,有人提出了十字鏈表。

    定點表:

    clip_image015

    其中firstin:入邊表頭指針,指向頂點入邊表的第一個結點。

    firstout:出邊表頭指針,指向頂點出邊表第一個結點。

    邊表:

    clip_image016

    其中tailvex是指弧起點在頂點表的下標,headvex弧終點在頂點表的下標,headlink入邊表指針域,指向終點相同的下一條邊,taillink是指邊表指針域,指向起點相同的下一條邊。

    clip_image017

    4、鄰接多重表

    鄰接多重表結構如圖:

    clip_image018

    ivex和jvex是與某條邊依附的兩個頂點在頂點表中的下標。ilink指向依附項點ivex的下一條邊,jlink指向依附頂點jvex的下一條邊。

     

    三 、代碼實現

     

    #include <iostream>
    
    #define  MAXVEX 100      //最大頂點數
    #define INFINITY 65535   //最大權值
    
    typedef int EdgeType;    //權值類型自己定義
    typedef char VertexType;  //頂點類型自己定義
    #pragma once
    
    #pragma region 鄰接矩陣結構體
    typedef struct 
    {
        VertexType vex[MAXVEX];   //頂點表
        EdgeType arg[MAXVEX][MAXVEX];  ///權值表-鄰接矩陣
        int numVertexes,numEdges;  //圖中的邊數和頂點數
    }GraphArray;
    
    #pragma endregion
    
    #pragma region 鄰接表結構體
    //邊表結點
    typedef struct EdgeNode
    {
        int nNodevex;     //鄰接點的點表中結點的坐標
        EdgeType nNodeWeight;   //用於網圖中邊的權值
        EdgeNode* next;       //鏈域,指向下一個鄰接點
    }EdgeNode,*pEdgeNode;
    //頂點表結點
    typedef struct VertexNode
    {
        VertexType nNodeData;   //頂點表中存儲的數據
        pEdgeNode pFirstNode;   //頂點表和邊表中關聯指針,指向邊表頭指針
    
    }VertexNode,pVertexNode,VertexList[MAXVEX];
    //圖結構
    typedef struct
    {
        VertexList vertexList;
        int numVertess,numEdges;
    }GraphList;
    
    #pragma endregion
    
    class GraphData
    {
    public:
        GraphData(void);
        ~GraphData(void);
        #pragma region 創建鄰接矩陣
        void CreateGraphArray(GraphArray* pGraphArray,int numVer,int numEdegs);
        int GetGraphLocation(GraphArray* pGraphArrray,char chpoint);
        #pragma endregion
    
        #pragma region 創建鄰接表
        void CreateGraphList(GraphList* pList,int numVer,int numEdegs);
        int GetGraphListLocation(GraphList* pList,char chpoint);
        #pragma endregion
    
    };
    Graphdata.h

     

  • #include "GraphData.h"
    
    
    GraphData::GraphData(void)
    {
    }
    
    
    GraphData::~GraphData(void)
    {
    }
    
    int GraphData::GetGraphLocation(GraphArray* pGraphArrray,char chpoint)
    {
        int i = 0;
        for (i = 0;i< pGraphArrray->numVertexes;i++)
        {
            if (pGraphArrray->vex[i] == chpoint)
            {
                break;;
            }
        }
        if (i >= pGraphArrray->numVertexes)
        {
            return -1;
        }
        return i;
    }
    /// <summary>
    /// 創建鄰接矩陣
    /// </summary>        
    void GraphData::CreateGraphArray(GraphArray* pGraphArray,int numVer,int numEdegs)
    {
        int weight = 0;
        pGraphArray->numVertexes = numVer;
        pGraphArray->numEdges = numEdegs;
        
        //創建頂點表
        for (int i= 0; i < numVer;i++)
        {
            pGraphArray->vex[i] = getchar();
            while(pGraphArray->vex[i] == '\n')
            {
                pGraphArray->vex[i] = getchar();
            }
        }
    
        //創建鄰接表的邊矩陣
        for (int i = 0; i < numEdegs; i++)
        {
            for (int j = 0;j < numEdegs ; j++)
            {
                pGraphArray->arg[i][j] = INFINITY;
            }        
        }
        for(int k = 0; k < pGraphArray->numEdges; k++)
        {
            char p, q;
            printf("輸入邊(vi,vj)上的下標i,下標j和權值:\n");
    
            p = getchar();
            while(p == '\n')
            {
                p = getchar();
            }
            q = getchar();
            while(q == '\n')
            {
                q = getchar();
            }
            scanf("%d", &weight);    
    
            int m = -1;
            int n = -1;
            m = GetGraphLocation(pGraphArray, p);
            n = GetGraphLocation(pGraphArray, q);
            if(n == -1 || m == -1)
            {
                fprintf(stderr, "there is no this vertex.\n");
                return;
            }
            //getchar();
            pGraphArray->arg[m][n] = weight;
            pGraphArray->arg[n][m] = weight;  //因為是無向圖,矩陣對稱
        }
        
    }
    
    #pragma region
    void GraphData::CreateGraphList(GraphList* pList,int numVer,int numEdegs)
    {
        int weight = 0;
        GraphList *pGraphList = pList;
        pGraphList->numVertess = numVer;
        pGraphList->numEdges = numEdegs;
        EdgeNode* firstNode,*secondNode;
        //創建頂點表
        for (int i= 0; i < numVer;i++)
        {
            pGraphList->vertexList[i].nNodeData = getchar();
            pGraphList->vertexList[i].pFirstNode = NULL;
            while(pGraphList->vertexList[i].nNodeData == '\n')
            {
                pGraphList->vertexList[i].nNodeData = getchar();
            }
        }
    
        //創建邊表    
        for(int k = 0; k < pGraphList->numEdges; k++)
        {
            char p, q;
            printf("輸入邊(vi,vj)上的下標i,下標j和權值:\n");
    
            p = getchar();
            while(p == '\n')
            {
                p = getchar();
            }
            q = getchar();
            while(q == '\n')
            {
                q = getchar();
            }
            scanf("%d", &weight);    
    
            int m = -1;
            int n = -1;
            m = GetGraphListLocation(pGraphList, p);
            n = GetGraphListLocation(pGraphList, q);
            if(n == -1 || m == -1)
            {
                fprintf(stderr, "there is no this vertex.\n");
                return;
            }
            //getchar();
            //字符p在頂點表的坐標為m,與坐標n的結點建立聯系權重為weight
            firstNode = new EdgeNode();
            firstNode->nNodevex = n;
            firstNode->next = pGraphList->vertexList[m].pFirstNode;
            firstNode->nNodeWeight = weight;
            pGraphList->vertexList[m].pFirstNode = firstNode;
    
            //第二個字符second
            secondNode = new EdgeNode();
            secondNode->nNodevex = m;
            secondNode->next = pGraphList->vertexList[n].pFirstNode;
            secondNode->nNodeWeight = weight;
            pGraphList->vertexList[n].pFirstNode = secondNode;
    
        }
    }
    
    int GraphData::GetGraphListLocation(GraphList* pList,char chpoint)
    {
        GraphList *pGraphList = pList;
        int i = 0;
        for (i = 0;i< pGraphList->numVertess;i++)
        {
            if (pGraphList->vertexList[i].nNodeData == chpoint)
            {
                break;;
            }
        }
        if (i >= pGraphList->numVertess)
        {
            return -1;
        }
        return i;
    }
    
    #pragma endregion
    GraphData.cpp
    #include <iostream>
    #include "GraphData.h"
    using namespace std;
    //
    
    void PrintGrgph(GraphList *pGraphList)
    {
        int i =0;
        while(pGraphList->vertexList[i].pFirstNode != NULL && i<MAXVEX)
        {
            printf("頂點:%c  ",pGraphList->vertexList[i].nNodeData);
            EdgeNode *e = NULL;
            e = pGraphList->vertexList[i].pFirstNode;
            while(e != NULL)
            {
                printf("%d  ", e->nNodevex);
                e = e->next;
            }
            i++;
            printf("\n");
        }
        
    }
    int main()
    {
        int numVexs,numEdges;
        GraphData* pTestGraph = new GraphData();
        GraphArray graphArray;
        GraphArray* pGraphArray = &graphArray;
        GraphList* pGgraphList = new GraphList();
        
        cout<<"輸入頂點數和邊數"<<endl;
        cin>>numVexs>>numEdges;
        cout<<"頂點數和邊數為:"<<numVexs<<numEdges<<endl;
    
        /*pTestGraph->CreateGraphArray(pGraphArray,numVexs,numEdges);
        for(int i = 0; i< numEdges;i++)
        {
            for (int j = 0;j< numEdges;j++)
            {
                cout<<pGraphArray->arg[i][j];
            }
            cout<<endl;
        }*/
        pTestGraph->CreateGraphList(pGgraphList,numVexs,numEdges);
        PrintGrgph(pGgraphList);
        system("pause");
    }
    TestGraph

     


免責聲明!

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



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