線性表和樹兩類數據結構,線性表中的元素是“一對一”的關系,樹中的元素是“一對多”的關系,本章所述的圖結構中的元素則是“多對多”的關系。圖(Graph)是一種復雜的非線性結構,在圖結構中,每個元素都可以有零個或多個前驅,也可以有零個或多個后繼,也就是說,元素之間的關系是任意的。
一、圖的定義與術語
定義:圖(Graph)是由頂點的有窮非空集合和頂點之間邊的集合組成,通常表示為:G(V,E),其中,G表示一個圖,V是圖G中頂點的集合,E是圖G中邊的集合。
1、圖的分類
圖是按照無方向和有方向分為無向圖和有向圖。
左圖為無向圖是由頂點和邊構成,右圖為有向圖是由頂點和弧(有向邊構成)。弧有弧頭和弧尾區別。
按照邊分為稀疏圖和稠密圖,這是個模糊的概念,同樣是相對的概念。
如果任意兩個頂點之間都存在邊叫完全圖,有向的邊叫有向完全圖。如果無重復的邊或者頂點到自身的邊叫簡單圖。在用數學方式表示時,無向邊用()表示,有向邊用<>表示。現在我們講解的圖全是簡單圖。
左圖沒有重復的邊或者到自身的邊(簡單圖),右圖則有。
這種邊帶權值的圖叫網
2.圖的頂點和邊間關系
頂點的度:頂點關聯邊的數目。有向圖圖中有,入度:方向指向頂點的邊;出度:方向背向頂點的邊。在有向圖中頂點的度就是兩者之和。
路徑長度:路徑上邊或者弧的數目。
左圖中,從B到D的路徑度為2,在右圖中就是3了(粗線的邊)。
右圖中A的入度是2,出度是1;B的入度為0,出度是2.
連通
在無向圖G中,任意兩個頂點是相通的就是連通圖。
左圖不是連通圖,AE之間沒有連通。
二、圖的存儲結構
圖的結構比價復雜,任意兩個頂點之間都可能存在關系,不能用簡單的順序存儲結構來表示。如果運用多重鏈表,即一個數據域多個指針域組成的結點表示圖中一個結點,則造成大量存儲單元浪費。
1、鄰接矩陣
鄰接矩陣用兩個數組保存數據。一個一維數組存儲圖中頂點信息,一個二維數組存儲圖中邊或弧的信息。
無向圖中二維數組是個對稱矩陣。
特點:
- 1、0表示無邊,1表示有邊
2、頂點的度是行內數組之和。
3、求取頂點鄰接點,將行內元素遍歷下。
有向圖的鄰接矩陣:有向圖中講究入度和出度,各行之和是出度,各列之和是入度。
帶權的圖叫網,用鄰接矩陣表示為:
鄰接矩陣對於邊數相對頂點較少的圖,就是對存儲空間極大的浪費。
2、鄰接表
鄰接表:數組和鏈表相結合的存儲方法為鄰接表。
- 圖中頂點用一個一維數組存儲。
- 圖中每個頂點Vi的所有鄰接點構成一個線性表。
從圖中得知,頂點表的各個結點由data和Firstedge兩個域表示,data是數據域,存儲頂點信息,firstedge是指針域,指向邊表的第一個結點,即頂點的第一個鄰接點。邊表結點由adjvex和next兩個域組成。adjvex是鄰接點域,存儲某頂點的鄰接點在頂點表中坐標,next存儲邊表中下一個結點指針。比如v1頂點與v2、v0互為鄰接點,則在v1邊表中,adjvex分別為0和2。
有向圖也可以用鄰接表,出度表叫鄰接表,入度表尾逆鄰接表。
3、十字鏈表
在鄰接表中針對有向圖,分為鄰接表和逆鄰接表,導致無法從一個表中獲取圖的入讀和出度的情況,有人提出了十字鏈表。
定點表:
其中firstin:入邊表頭指針,指向頂點入邊表的第一個結點。
firstout:出邊表頭指針,指向頂點出邊表第一個結點。
邊表:
其中tailvex是指弧起點在頂點表的下標,headvex弧終點在頂點表的下標,headlink入邊表指針域,指向終點相同的下一條邊,taillink是指邊表指針域,指向起點相同的下一條邊。
4、鄰接多重表
鄰接多重表結構如圖:
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 };
-
#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
#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"); }