【算法與數據結構】圖 -- 十字鏈表

 

圖的【十字鏈表】表示法是一種鏈式存儲結構,能夠當作是【鄰接表】和【逆鄰接表】的組合node

本文中用到的有向圖數組

 

 

 

/************************************************************************ 有向圖的存儲:十字鏈表 有向圖的十字鏈表存儲結構,是有一種鏈式存儲結構,能夠當作是【鄰接表】和【逆鄰接表】 的結合。 圖中每條弧對應一個【弧結點】,每一個頂點對應一個【頂點結點】 弧結點 -------------------------------------------- | tailvex | headvex | hlink | tlink | info | -------------------------------------------- talivex:以該弧爲【弧尾】的結點在圖中的位置 headvex:以該弧爲【弧頭】的結點在圖中的位置 hlink: 下一條與該弧有【相同弧頭的弧】 tlink: 下一條與該弧有【相同弧尾的弧】 info: 弧的相關信息,權值等 頂點結點 ----------------------------- | data | firstin | firstout | ----------------------------- data: 該結點的數據 firstin: 第一條以該弧爲弧頭的【弧結點】 firstout:第一條以該弧爲弧尾的【弧結點】 ************************************************************************/

 

 

 

相關數據結構數據結構

//頂點結點最大數量 int const MAX_VERTEXNUM = 100; //數據結構  typedef int InfoType; typedef int Data; //弧結點 typedef struct _tagArcBox { int tailvex; //該弧的弧尾結點在圖中的位置 int headvex; //該弧的弧頭結點在圖中的位置 struct _tagArcBox* hlink; //下一條與該弧有相同弧頭結點的弧 struct _tagArcBox* tlink; //下一條與該弧有相同弧尾結點的弧 InfoType info; //弧的相關信息 }ArcBox; //頂點結點 typedef struct _tagArcNode { Data data; //數據 ArcBox* firstin; //第一條以該節點爲弧尾的弧 ArcBox* firstout; //第一條以該結點爲弧頭的弧 }ArcNode; //十字鏈表存儲結構的有向圖 typedef struct _tagOLGraph { ArcNode vertex[MAX_VERTEXNUM]; //頂點向量  int vexnum; //頂點數 int arcnum; //弧樹   }OLGraph, *POLGraph;

 

 

 

從頂點向量中查找該頂點在圖中的位置(下標)函數

//輸入圖的【頂點向量】和某個頂點的數據 //獲取此頂點在頂點向量中的位置(下標) int LocateNode(ArcNode* pNodesArr, Data data, int length) { if( NULL == pNodesArr ) return NULL; for (int i = 0; i < length; ++ i) { if (pNodesArr[i].data == data) return i; } return -1; } //輸入【圖】和某頂點的數據 //獲取該頂點在頂點向量中的位置(下標) int LocateNode(POLGraph& pOLGraph, Data data) { return (NULL == pOLGraph ? NULL : LocateNode(pOLGraph->vertex, data, pOLGraph->vexnum) ); }

 

 

有向圖的建立spa

/************************************************************************ 當使用這種函數原型時: void CreateOLGraph(POLGPraph& pOLGraph) { pOLGraph = new OLGraph(); //codes pOLGraph->vexnum = num; //這裏報運行時錯誤! why? } ************************************************************************/ //建立十字鏈表有向圖  POLGraph CreateOLGraph() { POLGraph pOlGraph = NULL; __try { pOlGraph = new OLGraph(); if(NULL == pOlGraph) {cerr << "申請圖結點失敗!\n"; return NULL;} int num = 0; cout << "請輸入圖的頂點數量:"; cin >> num; pOlGraph->vexnum = num; cout << endl; cout << "請輸入圖的弧的數量:"; cin >> num; pOlGraph->arcnum = num; cout << endl; Data data = 0; cout << endl << "--------開始 初始化頂點向量-----------"<<endl; for (int i = 0; i < pOlGraph->vexnum; ++i) { cout << "請輸入結點的值:"; cin >> data; pOlGraph->vertex[i].data = data; pOlGraph->vertex[i].firstin = NULL; pOlGraph->vertex[i].firstout = NULL; cout<<endl; } //for cout <<endl<<"------------結束 初始化頂點向量------------"<<endl; cout<<endl<<endl; cout << "************開始 初始化弧結點 **************"<<endl; for(int i = 0; i < pOlGraph->arcnum; ++ i) { cout << "請輸入弧的弧尾結點:"; cin >> data; int begin = LocateNode(pOlGraph->vertex, data, pOlGraph->arcnum); if(-1 == begin) {cerr << "您輸入的弧尾結點不在圖中,請從新輸入"<<endl; --i; continue;} cout << "請輸入弧的弧頭結點:"; cin >> data; int end = LocateNode(pOlGraph->vertex, data, pOlGraph->arcnum); if(-1 == end) {cerr << "您輸入的弧頭結點不在圖中,請從新輸入"<<endl; -- i; continue;} cout << "請輸入弧的權值:"; cin >> data; cout<<endl<<endl; ArcBox* pArcBox = new ArcBox(); if(NULL == pArcBox) {cerr << "申請弧結點失敗!"<<endl; -- i; continue;} pArcBox->tailvex = begin; //該弧的弧尾在圖中的位置 pArcBox->headvex = end; //該弧的弧頭在圖中的位置 pArcBox->hlink = pOlGraph->vertex[end].firstin; //下一條與該弧有相同弧尾的弧結點 pArcBox->tlink = pOlGraph->vertex[begin].firstout; //下一條與該弧有相同弧頭的弧結點 pArcBox->info = data; //權值  pOlGraph->vertex[begin].firstout = pOlGraph->vertex[end].firstin = pArcBox; } //for  } //__try  __except(1) { cerr << endl<<"有異常發生"<<endl; } return pOlGraph; }

 

 

運行狀況:指針

 

 

 

 

出度和入度 code

//求圖的出度 //先根據輸入的頂點的值,求得該點所在的頂點向量的份量 //而後獲得該點的firstout,而後再獲得全部與該弧有相同弧尾 //結點的弧(的條數) int OutDegree(POLGraph& pOLGraph, Data data) { int nCount = 0; //根據結點的值定位該點在圖的頂點向量中的位置(下標) int nIndex = LocateNode(pOLGraph, data); if(-1 == nIndex) {cerr << "該點不在圖中,因此沒有出度!\n"<<endl; return -1;} //獲得該結點指針 ArcNode* pArcNode = &(pOLGraph->vertex[nIndex]); if(NULL == pArcNode) {cerr << "在圖中查找該頂點出錯!\n"<<endl; return -1;} //第一條該頂點爲弧尾的弧(該頂點結點的第一條出邊) ArcBox* pArcBox = pArcNode->firstout; //查找全部以該【頂點結點】爲【弧尾】的【弧結點】 while(NULL != pArcBox) { ++ nCount; pArcBox = pArcBox->tlink; } return nCount; } //定點的入度 int InDegree(POLGraph& pOLGraph, Data data) { int nCount = 0; //定位該頂點結點在頂點向量中的位置(下標) int nIndex = LocateNode(pOLGraph, data); if(-1 == nIndex){cerr << "該點不在圖中,因此沒有入度!\n"<<endl; return -1;} //獲得該頂點結點的指針 ArcNode* pArcNode = &(pOLGraph->vertex[nIndex]); if(NULL == pArcNode) {cerr << "在圖中查找該點出錯!"<<endl; return -1;} //第一條以該頂點結點爲弧頭的弧(該頂點結點的第一條入邊) ArcBox* pArcBox = pArcNode->firstin; //查找全部以該【頂點結點】爲【弧頭】的【弧結點】 while(NULL != pArcBox) { ++nCount; pArcBox = pArcBox->hlink; } return nCount; }

 

 

 查找出度 / 入度blog

 POLGraph pGraph = CreateOLGraph(); int num = 0; for(int i = 0; i < pGraph->vexnum + 3; ++ i) { cout << "請輸入帶查的頂點的值:"; cin >> num; cout<<"結點 "<< num << " 的出度OutDegree爲:"<<OutDegree(pGraph, num); cout<<endl; cout<<"結點 "<< num << " 的入度InDegree爲:"<<InDegree(pGraph, num); cout<<endl<<endl; }

 

 

 

 

 

 

 

 深度優先遍歷(DFS)  遞歸

/************************************************************************ 深度優先遍歷 從某頂點出發,訪問之,而後得到該頂點的第一條出邊(firstout),若是該出邊 不爲空,則得到該條出邊的【弧頭】結點在圖中的位置(下標),查看此下標的結點 是否被訪問過,若是沒有則根據此下標獲取該結點,而後遞歸訪問之;若是此結點 被訪問過了,則說明出現迴路,及此條弧指向了以前訪問過的結點,須要跳出循環, 不然出現死循環。 ************************************************************************/ //頂點結點是否遍歷過標誌數組 bool* pVisited = NULL; void DFS(POLGraph& pOLGraph, ArcNode* pArcNode) { int nIndex = LocateNode(pOLGraph, pArcNode->data); pVisited[nIndex] = true; cout << "the node is "<<pArcNode->data<<endl; ArcBox* pArcBox = pArcNode->firstout; while(NULL != pArcBox) { //該弧的弧頭在圖中的位置 int nHeadVex = pArcBox->headvex; if (pVisited[nHeadVex] == false) { ArcNode* pHeadNode = &(pOLGraph->vertex[nHeadVex]); DFS(pOLGraph, pHeadNode); } //若是某條弧的弧頭結點已經被訪問過期,則說明已經有了迴路,此時要跳出循環 //不然會在while中死循環 else { break; } } } //有向圖的深度優先遍歷 void DFSTraverse(POLGraph& pOLGraph) { if(NULL == pOLGraph) {cerr << "該圖爲空!"; return;} pVisited = new bool[pOLGraph->vexnum](); for (int i = 0; i < pOLGraph->vexnum; ++ i) pVisited[i] = false; for (int i = 0; i < pOLGraph->vexnum; ++ i) { if (! pVisited[i]) { DFS(pOLGraph, &pOLGraph->vertex[i]); } } }

 

 

深度優先遍歷結果 ci

 

 

 

 

 

 

 

 

廣度優先遍歷(BFS)

 關於廣度優先遍歷,我見到兩種寫法

這兩種寫法大體以下

 

方式1

for(int i = 0; i < 圖中點的數量; ++ i) { if(結點 i 沒有被訪問過) { 訪問之 入隊 while(隊不空) { 出隊 訪問隊頭結點全部鄰接點,將訪問過的鄰接點的訪問標誌數組的份量 置爲true; } 將該結點 i 置爲鄰接點訪問過 } } 

 

 

 

 

方式2

int nStart = 0; //從圖中位置爲nStart的結點開始遍歷 將該結點入隊 訪問該nStart結點 if(nStart結點的鄰接點沒有被訪問) { while(隊不空) { 隊頭元素出隊 while(隊頭元素仍然有出邊) { 訪問隊頭元素的全部出邊的弧頭結點,並置訪問過的結點的 訪問標誌置爲true } } //while  將該點的 鄰接點訪問標誌 置爲true } //if 

 

 

 

 

 

 下面是這兩種方式的代碼實現,圖的存儲結構是十字鏈表(有向圖的存儲結構)

 

//----------------------------方式1------------------------------------ //有向圖的廣度優先遍歷  void BFS(POLGraph& pOLGraph) { if(NULL == pOLGraph) return; bool *pVisitedLinjie = new bool[pOLGraph->vexnum]; pVisited = new bool[pOLGraph->vexnum]; for(int i = 0; i < pOLGraph->vexnum; ++ i){pVisited[i] = false; pVisitedLinjie[i] = false;} queue<ArcNode*> quArcNode; for (int i = 0; i < pOLGraph->vexnum; ++ i) { if (pVisited[i] == false) { cout << "data of node is "<<pOLGraph->vertex[i].data<<endl; quArcNode.push(&(pOLGraph->vertex[i])); while(! quArcNode.empty()) { //取隊頭元素, 並出隊 ArcNode* pArcNode = quArcNode.front(); quArcNode.pop(); //定位該隊頭元素在圖中的位置 int nIndex = LocateNode(pOLGraph, pArcNode->data); if(-1 == nIndex) return; //若是該結點的【鄰接點】【未被訪問】 if (pVisitedLinjie[nIndex] == false) { //訪問其全部鄰接點 //找隊頭元素的第一條【出邊】 ArcBox* pArcBox = pArcNode->firstout; while(NULL != pArcBox) { //定位該弧的【弧頭結點】在圖中的位置 int nHead = pArcBox->headvex; //若是該弧頭節點【未被訪問】,則訪問之,若是已被訪問則說明出現了環 if (false == pVisited[nHead]) { cout << "data of node is "<<pOLGraph->vertex[nHead].data<<endl; //將該弧頭節點置爲【訪問過】 pVisited[nHead] = true; //將該結點 入隊 quArcNode.push(&(pOLGraph->vertex[nHead])); //下一條與該弧有相同【弧尾】的弧 pArcBox = pArcBox->tlink; } else { //出現了環,退出 break; } } //到這,該節點的全部鄰接點都訪問過了 //將其鄰接點訪問標記 標爲【訪問過】  pVisitedLinjie[nIndex] = true; } //if   } //while  } } }

 

 

 

 

 

 

//------------------------------方式2------------------------------------ //nStart爲從圖中位置爲nStart的結點開始遍歷 void BFS(POLGraph& pOLGraph, int nStart) { if(NULL == pOLGraph) return; bool *pVisitedLinjie = new bool[pOLGraph->vexnum]; pVisited = new bool[pOLGraph->vexnum]; for(int i = 0; i < pOLGraph->vexnum; ++ i){pVisited[i] = false; pVisitedLinjie[i] = false;} cout << "data of node is "<<pOLGraph->vertex[nStart].data<<endl; pVisited[nStart] = true; queue<ArcNode*> quArcNode; quArcNode.push(&(pOLGraph->vertex[nStart])); if (pVisited[nStart] == false) { cout << "data of node is "<<pOLGraph->vertex[i].data<<endl; quArcNode.push(&(pOLGraph->vertex[i])); while(! quArcNode.empty()) { //取隊頭元素, 並出隊 ArcNode* pArcNode = quArcNode.front(); quArcNode.pop(); //定位該隊頭元素在圖中的位置 int nIndex = LocateNode(pOLGraph, pArcNode->data); if(-1 == nIndex) return; //若是該結點的【鄰接點】【未被訪問】 if (pVisitedLinjie[nIndex] == false) { //訪問其全部鄰接點 //找隊頭元素的第一條【出邊】 ArcBox* pArcBox = pArcNode->firstout; while(NULL != pArcBox) { //定位該弧的【弧頭結點】在圖中的位置 int nHead = pArcBox->headvex; //若是該弧頭節點【未被訪問】,則訪問之,若是已被訪問則說明出現了環 if (false == pVisited[nHead]) { cout << "data of node is "<<pOLGraph->vertex[nHead].data<<endl; //將該弧頭節點置爲【訪問過】 pVisited[nHead] = true; //將該結點 入隊 quArcNode.push(&(pOLGraph->vertex[nHead])); //下一條與該弧有相同【弧尾】的弧 pArcBox = pArcBox->tlink; } else { //出現了環,退出 break; } } //到這,該節點的全部鄰接點都訪問過了 //將其鄰接點訪問標記 標爲【訪問過】  pVisitedLinjie[nIndex] = true; } //if   } //while } //if  }
相關文章
相關標籤/搜索