圖,主要內容:深度優先遍歷,廣度優先遍歷,最小生成樹。文中代碼均已在VS2015上測試,空指針均為nullptr(C++11)。參考來源:慕課網
圖
分類:有向圖。無向圖
頂點,弧,弧頭。弧尾,權值,出度,入度,頂點,邊,鄰接點,連通圖,完全圖【邊數=n(n-1)/2 】,生成樹【邊數=n-1】
核心:圖的遍歷,最小生成樹
存儲結構
圖的存儲結構:鄰接矩陣,鄰接表,十字鏈表,鄰接多重表
鄰接矩陣--數組存儲
struct Node
{
頂點索引;
頂點數據;
};
struct Map
{
頂點數組;
鄰接矩陣;
};
鄰接表--鏈式存儲
struct Node
{
頂點索引;
該頂點弧鏈表的頭結點;
頂點數據;
}
struct Arc
{
指向的頂點索引;
指向下一條弧的指針;
弧信息;
}
struct Map
{
頂點數組;
}
十字鏈表--鏈式存儲
struct Node
{
頂點索引;
頂點數據;
第一條入弧結點指針;
第一條出弧結點指針;
}
struct Arc
{
弧尾頂點索引;
弧頭頂點索引;
指向下一條弧頭相同的弧的指針;
指向下一條弧尾相同的弧的指針;
弧信息;
}
struct Map
{
頂點數組;
}
鄰接多重表--鏈式存儲(無向圖)
struct Node
{
頂點索引;
頂點數據;
第一條邊結點指針;
}
struct Arc
{
頂點A索引;
頂點B索引;
連接A的下一條邊的指針;
連接A的下一條邊的指針;
邊信息;
}
struct Map
{
頂點數組;
}
圖的遍歷
深度優先搜索(前序遍歷,根左右),廣度優先搜索(層級搜索)
A
/ \
B D
/ \ / \
C F G - H
\ /
E
深度優先遍歷:A B C E F D G H
廣度優先遍歷:A B D C F G H E
鄰接矩陣遍歷代碼:
【Node.h】
#ifndef NODE_H
#define NODE_H
class Node
{
public:
Node(char data = 0);
char m_cData;
bool m_bIsVisited;
};
#endif // !NODE_H
【Node.cpp】
#include "Node.h"
Node::Node(char data)
{
m_cData = data;
m_bIsVisited = false;
}
【CMap.h】
#ifndef CMAP_H
#define CMAP_H
#include <vector>
#include "Node.h"
#include <iostream>
using namespace std;
class CMap
{
public:
CMap(int capacity);
~CMap();
bool addNode(Node *pNode);//向圖中加入頂點
void resetNode();//重置頂點
bool setValueToMatrixForDirectedGraph(int row, int col, int val = 1);//為有向圖設置鄰接矩陣
bool setValueToMatrixForUndirectedGraph(int row, int col, int val = 1);//為無向圖設置鄰接矩陣
void printMatrix();//打印鄰接矩陣
void depthFirstTraverse(int nodeIndex);//深度優先遍歷
void breadthFirstTraverse(int nodeIndex);//廣度優先遍歷
private:
bool getValueFromMatrix(int row, int col, int &val);//從矩陣中獲取權值
void breadthFirstTraverseImpl(vector<int>preVec);//廣度優先遍歷實現函數
private:
int m_iCapacity;//圖中最多可以容納的頂點數
int m_iNodeCount;//已經添加的頂點個數
Node *m_pNodeArray;//用來存放頂點數組
int *m_pMatrix;//用來存放鄰接矩陣
};
#endif // !CMAP_H
【CMap.cpp】
#include "CMap.h"
CMap::CMap(int capacity)
{
m_iCapacity = capacity;
m_iNodeCount = 0;
m_pNodeArray = new Node[m_iCapacity];
m_pMatrix = new int[m_iCapacity *m_iCapacity];
memset(m_pMatrix, 0, m_iCapacity*m_iCapacity * sizeof(int));
/*for (int i = 0; i < m_iCapacity *m_iCapacity; i++)
{
m_pMatrix[i] = 0;
}*/
}
CMap::~CMap()
{
delete[]m_pNodeArray;
delete[]m_pMatrix;
}
bool CMap::addNode(Node * pNode)
{
if (pNode == nullptr)
{
return false;
}
m_pNodeArray[m_iNodeCount].m_cData = pNode->m_cData;
m_iNodeCount++;
return true;
}
void CMap::resetNode()
{
for (int i = 0;i < m_iNodeCount;i++)
{
m_pNodeArray[i].m_bIsVisited = false;
}
}
bool CMap::setValueToMatrixForDirectedGraph(int row, int col, int val)
{
if (row<0||row>=m_iCapacity)
{
return false;
}
if (col<0 || col >= m_iCapacity)
{
return false;
}
m_pMatrix[row *m_iCapacity + col] = val;
return true;
}
bool CMap::setValueToMatrixForUndirectedGraph(int row, int col, int val)
{
if (row<0 || row >= m_iCapacity)
{
return false;
}
if (col<0 || col >= m_iCapacity)
{
return false;
}
m_pMatrix[row *m_iCapacity + col] = val;
m_pMatrix[col *m_iCapacity + row] = val;
return true;
}
void CMap::printMatrix()
{
for (int i = 0; i < m_iCapacity; i++)
{
for (int k = 0; k < m_iCapacity; k++)
{
cout << m_pMatrix[i*m_iCapacity + k] << " ";
}
cout << endl;
}
}
void CMap::depthFirstTraverse(int nodeIndex)
{
int value = 0;
cout << m_pNodeArray[nodeIndex].m_cData << " ";
m_pNodeArray[nodeIndex].m_bIsVisited = true;
for (int i = 0;i < m_iCapacity;i++)
{
getValueFromMatrix(nodeIndex, i, value);
if (value == 1)
{
if (m_pNodeArray[i].m_bIsVisited)
{
continue;
}
else
{
depthFirstTraverse(i);
}
}
else
{
continue;
}
}
}
void CMap::breadthFirstTraverse(int nodeIndex)
{
cout << m_pNodeArray[nodeIndex].m_cData << " ";
m_pNodeArray[nodeIndex].m_bIsVisited = true;
vector<int>curVec;
curVec.push_back(nodeIndex);
breadthFirstTraverseImpl(curVec);
}
bool CMap::getValueFromMatrix(int row, int col, int & val)
{
if (row<0 || row >= m_iCapacity)
{
return false;
}
if (col<0 || col >= m_iCapacity)
{
return false;
}
val = m_pMatrix[row *m_iCapacity + col];
return true;
}
void CMap::breadthFirstTraverseImpl(vector<int> preVec)
{
int value = 0;
vector<int> curVec;
for (int j = 0; j < (int)preVec.size(); j++)
{
for (int i = 0; i < m_iCapacity; i++)
{
getValueFromMatrix(preVec[j], i, value);
if (value!=0)
{
if (m_pNodeArray[i].m_bIsVisited)
{
continue;
}
else
{
cout << m_pNodeArray[i].m_cData << " ";
m_pNodeArray[i].m_bIsVisited = true;
curVec.push_back(i);
}
}
}
}
if (curVec.size()==0)
{
return;
}
else
{
breadthFirstTraverseImpl(curVec);
}
}
【main.cpp】
#include "CMap.h"
int main(void)
{
CMap *pMap = new CMap(8);
Node *pNodeA = new Node('A');
Node *pNodeB = new Node('B');
Node *pNodeC = new Node('C');
Node *pNodeD = new Node('D');
Node *pNodeE = new Node('E');
Node *pNodeF = new Node('F');
Node *pNodeG = new Node('G');
Node *pNodeH = new Node('H');
pMap->addNode(pNodeA);
pMap->addNode(pNodeB);
pMap->addNode(pNodeC);
pMap->addNode(pNodeD);
pMap->addNode(pNodeE);
pMap->addNode(pNodeF);
pMap->addNode(pNodeG);
pMap->addNode(pNodeH);
pMap->setValueToMatrixForUndirectedGraph(0, 1);
pMap->setValueToMatrixForUndirectedGraph(0, 3);
pMap->setValueToMatrixForUndirectedGraph(1, 2);
pMap->setValueToMatrixForUndirectedGraph(1, 5);
pMap->setValueToMatrixForUndirectedGraph(3, 6);
pMap->setValueToMatrixForUndirectedGraph(3, 7);
pMap->setValueToMatrixForUndirectedGraph(6, 7);
pMap->setValueToMatrixForUndirectedGraph(2, 4);
pMap->setValueToMatrixForUndirectedGraph(2, 5);
pMap->printMatrix();
cout << endl;
pMap->depthFirstTraverse(0);
cout << endl;
pMap->resetNode();
pMap->breadthFirstTraverse(0);
cout << endl;
return 0;
}
最小生成樹
普里姆(Prim)算法;克魯斯卡爾(Kruskal)算法
普里姆算法
基本思想
普里姆算法的基本思想:普里姆算法是一種構造最小生成樹的算法,它是按逐個將頂點連通的方式來構造最小生成樹的。
從連通網絡N = { V, E }中的某一頂點u0出發,選擇與它關聯的具有最小權值的邊(u0, v),將其頂點加入到生成樹的頂點集合U中。以后每一步從一個頂點在U中,而另一個頂點不在U中的各條邊中選擇權值最小的邊(u, v),把該邊加入到生成樹的邊集TE中,把它的頂點加入到集合U中。如此重復執行,直到網絡中的所有頂點都加入到生成樹頂點集合U中為止。
假設G=(V,E)是一個具有n個頂點的帶權無向連通圖,T(U,TE)是G的最小生成樹,其中U是T的頂點集,TE是T的邊集,則構造G的最小生成樹T的步驟如下:
(1)初始狀態,TE為空,U={v0},v0∈V;
(2)在所有u∈U,v∈V-U的邊(u,v)∈E中找一條代價最小的邊(u′,v′)并入TE,同時將v′并入U;
重復執行步驟(2)n-1次,直到U=V為止。
在普里姆算法中,為了便于在集合U和(V-U)之間選取權值最小的邊,需要設置兩個輔助數組closest和lowcost,分別用于存放頂點的序號和邊的權值。
對于每一個頂點v∈V-U,closest[v]為U中距離v最近的一個鄰接點,即邊(v,closest[v])是在所有與頂點v相鄰、且其另一頂點j∈U的邊中具有最小權值的邊,其最小權值為lowcost[v],即lowcost[v]=cost[v][closest[v]],采用鄰接表作為存儲結構:
設置一個輔助數組closedge[]:
lowcost域存放生成樹頂點集合內頂點到生成樹外各頂點的各邊上的當前最小權值;
adjvex域記錄生成樹頂點集合外各頂點距離集合內哪個頂點最近(即權值最小)。
克魯斯卡爾算法
基本思想
克魯斯卡爾算法是在剩下的所有未選取的邊中,找最小邊,如果和已選取的邊構成回路,則放棄,選取次小邊。
克魯斯卡爾算法的時間復雜度為O(eloge)(e為網中邊的數目),因此它相對于普里姆算法而言,適合于求邊稀疏的網的最小生成樹。
實例
【Node.h】
#ifndef NODE_H
#define NODE_H
class Node
{
public:
Node(char data = 0);
char m_cData;
bool m_bIsVisited;
};
#endif // !NODE_H
【Node.cpp】
#include "Node.h"
Node::Node(char data)
{
m_cData = data;
m_bIsVisited = false;
}
【Edge.h】
#ifndef EDGE_H
#define EDGE_H
class Edge
{
public:
Edge(int nodeIndexA = 0, int nodeIndexB = 0, int weightValue = 0);
int m_iNodeIndexA;
int m_iNodeIndexB;
int m_iWeightValue;
bool m_bSelected;
};
#endif // !EDGE_H
【Edge.cpp】
#include "Edge.h"
Edge::Edge(int nodeIndexA, int nodeIndexB, int weightValue)
{
m_iNodeIndexA = nodeIndexA;
m_iNodeIndexB = nodeIndexB;
m_iWeightValue = weightValue;
m_bSelected = false;
}
【CMap.h】
#ifndef CMAP_H
#define CMAP_H
#include <vector>
#include "Node.h"
#include "Edge.h"
#include <iostream>
using namespace std;
class CMap
{
public:
CMap(int capacity);
~CMap();
bool addNode(Node *pNode);//向圖中加入頂點
void resetNode();//重置頂點
bool setValueToMatrixForDirectedGraph(int row, int col, int val = 1);//為有向圖設置鄰接矩陣
bool setValueToMatrixForUndirectedGraph(int row, int col, int val = 1);//為無向圖設置鄰接矩陣
void printMatrix();//打印鄰接矩陣
void depthFirstTraverse(int nodeIndex);//深度優先遍歷
void breadthFirstTraverse(int nodeIndex);//廣度優先遍歷
void primTree(int nodeIndex);//普里姆生成樹
void kruskalTree();
private:
bool getValueFromMatrix(int row, int col, int &val);//從矩陣中獲取權值
void breadthFirstTraverseImpl(vector<int>preVec);//廣度優先遍歷實現函數
int getMinEdge(vector<Edge>edgeVec);
bool isInSet(vector<int>nodeSet, int target);
void mergeNodeSet(vector<int>&nodeSetA, vector<int>nodeSetB);
private:
int m_iCapacity;//圖中最多可以容納的頂點數
int m_iNodeCount;//已經添加的頂點個數
Node *m_pNodeArray;//用來存放頂點數組
int *m_pMatrix;//用來存放鄰接矩陣
Edge *m_pEdge;
};
#endif // !CMAP_H
【CMap.cpp】
#include "CMap.h"
CMap::CMap(int capacity)
{
m_iCapacity = capacity;
m_iNodeCount = 0;
m_pNodeArray = new Node[m_iCapacity];
m_pMatrix = new int[m_iCapacity *m_iCapacity];
memset(m_pMatrix, 0, m_iCapacity*m_iCapacity * sizeof(int));
/*for (int i = 0; i < m_iCapacity *m_iCapacity; i++)
{
m_pMatrix[i] = 0;
}*/
m_pEdge = new Edge[m_iCapacity - 1];
}
CMap::~CMap()
{
delete[]m_pNodeArray;
delete[]m_pMatrix;
}
bool CMap::addNode(Node * pNode)
{
if (pNode == nullptr)
{
return false;
}
m_pNodeArray[m_iNodeCount].m_cData = pNode->m_cData;
m_iNodeCount++;
return true;
}
void CMap::resetNode()
{
for (int i = 0;i < m_iNodeCount;i++)
{
m_pNodeArray[i].m_bIsVisited = false;
}
}
bool CMap::setValueToMatrixForDirectedGraph(int row, int col, int val)
{
if (row<0||row>=m_iCapacity)
{
return false;
}
if (col<0 || col >= m_iCapacity)
{
return false;
}
m_pMatrix[row *m_iCapacity + col] = val;
return true;
}
bool CMap::setValueToMatrixForUndirectedGraph(int row, int col, int val)
{
if (row<0 || row >= m_iCapacity)
{
return false;
}
if (col<0 || col >= m_iCapacity)
{
return false;
}
m_pMatrix[row *m_iCapacity + col] = val;
m_pMatrix[col *m_iCapacity + row] = val;
return true;
}
void CMap::printMatrix()
{
for (int i = 0; i < m_iCapacity; i++)
{
for (int k = 0; k < m_iCapacity; k++)
{
cout << m_pMatrix[i*m_iCapacity + k] << " ";
}
cout << endl;
}
}
void CMap::depthFirstTraverse(int nodeIndex)
{
int value = 0;
cout << m_pNodeArray[nodeIndex].m_cData << " ";
m_pNodeArray[nodeIndex].m_bIsVisited = true;
for (int i = 0;i < m_iCapacity;i++)
{
getValueFromMatrix(nodeIndex, i, value);
if (value == 1)
{
if (m_pNodeArray[i].m_bIsVisited)
{
continue;
}
else
{
depthFirstTraverse(i);
}
}
else
{
continue;
}
}
}
void CMap::breadthFirstTraverse(int nodeIndex)
{
cout << m_pNodeArray[nodeIndex].m_cData << " ";
m_pNodeArray[nodeIndex].m_bIsVisited = true;
vector<int>curVec;
curVec.push_back(nodeIndex);
breadthFirstTraverseImpl(curVec);
}
void CMap::primTree(int nodeIndex)
{
int value = 0;
int edgeCount = 0;
vector<int>nodeVec;
vector<Edge>edgeVec;
cout << m_pNodeArray[nodeIndex].m_cData << endl;
m_pNodeArray[nodeIndex].m_bIsVisited = true;
nodeVec.push_back(nodeIndex);
while (edgeCount < m_iCapacity - 1)
{
int temp = nodeVec.back();
for (int i = 0; i < m_iCapacity; i++)
{
getValueFromMatrix(temp, i, value);
if (value != 0)
{
if (m_pNodeArray[i].m_bIsVisited)
{
continue;
}
else
{
Edge edge(temp, i, value);
edgeVec.push_back(edge);
}
}
}
int edgeIndex = getMinEdge(edgeVec);
edgeVec[edgeIndex].m_bSelected = true;
cout << edgeVec[edgeIndex].m_iNodeIndexA << "---" << edgeVec[edgeIndex].m_iNodeIndexB << " ";
cout << edgeVec[edgeIndex].m_iWeightValue << endl;
m_pEdge[edgeCount] = edgeVec[edgeIndex];
edgeCount++;
int nextNodeIndex = edgeVec[edgeIndex].m_iNodeIndexB;
nodeVec.push_back(nextNodeIndex);
m_pNodeArray[nextNodeIndex];
m_pNodeArray[nextNodeIndex].m_bIsVisited = true;
cout << m_pNodeArray[nextNodeIndex].m_cData << endl;
}
}
void CMap::kruskalTree()
{
int value = 0;
int edgeCount = 0;
//定義存放結點集合的數組
vector<vector<int>>nodeSets;
//第一步:取出所有邊
vector<Edge>edgeVec;
for (int i=0;i<m_iCapacity;i++)
{
for (int k = i+1;k<m_iCapacity;k++)
{
getValueFromMatrix(i, k, value);
if (value!=0)
{
Edge edge(i, k, value);
edgeVec.push_back(edge);
}
}
}
//第二步:從所有邊中取出組成最小生成樹的邊
//1.找到算法結束條件
while (edgeCount<m_iCapacity-1)
{
//2.從邊集合中找到最小邊
int minEdgeIndex = getMinEdge(edgeVec);
edgeVec[minEdgeIndex].m_bSelected = true;
//3.找到最小邊連接的點
int nodeAIndex = edgeVec[minEdgeIndex].m_iNodeIndexA;
int nodeBIndex = edgeVec[minEdgeIndex].m_iNodeIndexB;
//4.找到點所在的點集合
bool nodeAIsSet = false;
bool nodeBIsSet = false;
int nodeAInSetLabel = -1;
int nodeBInSetLabel = -1;
for (int i = 0;i<(int)nodeSets.size();i++)
{
nodeAIsSet = isInSet(nodeSets[i], nodeAIndex);
if (nodeAIsSet)
{
nodeAInSetLabel = i;
}
}
for (int i = 0;i < (int)nodeSets.size();i++)
{
nodeBIsSet = isInSet(nodeSets[i], nodeBIndex);
if (nodeBIsSet)
{
nodeBInSetLabel = i;
}
}
//5.根據點所在集合的不同做出不同處理
if (nodeAInSetLabel==-1&&nodeBInSetLabel==-1)
{
vector<int>vec;
vec.push_back(nodeAIndex);
vec.push_back(nodeBIndex);
nodeSets.push_back(vec);
}
else if (nodeAInSetLabel!=-1&&nodeBInSetLabel==-1)
{
nodeSets[nodeAInSetLabel].push_back(nodeBIndex);
}
else if (nodeAInSetLabel != -1 && nodeBInSetLabel != -1 && nodeAInSetLabel != nodeBInSetLabel)
{
mergeNodeSet(nodeSets[nodeAInSetLabel], nodeSets[nodeBInSetLabel]);
for (int k = nodeBInSetLabel;k < (int)nodeSets.size() - 1;k++)
{
nodeSets[k] = nodeSets[k + 1];
}
}
else if (nodeAInSetLabel!=-1&&nodeBInSetLabel!=-1&&nodeAInSetLabel==nodeBInSetLabel)
{
continue;
}
m_pEdge[edgeCount] = edgeVec[minEdgeIndex];
edgeCount++;
cout << edgeVec[minEdgeIndex].m_iNodeIndexA << "---" << edgeVec[minEdgeIndex].m_iNodeIndexB << " ";
cout << edgeVec[minEdgeIndex].m_iWeightValue << endl;
}
}
bool CMap::getValueFromMatrix(int row, int col, int & val)
{
if (row<0 || row >= m_iCapacity)
{
return false;
}
if (col<0 || col >= m_iCapacity)
{
return false;
}
val = m_pMatrix[row *m_iCapacity + col];
return true;
}
void CMap::breadthFirstTraverseImpl(vector<int> preVec)
{
int value = 0;
vector<int> curVec;
for (int j = 0; j < (int)preVec.size(); j++)
{
for (int i = 0; i < m_iCapacity; i++)
{
getValueFromMatrix(preVec[j], i, value);
if (value!=0)
{
if (m_pNodeArray[i].m_bIsVisited)
{
continue;
}
else
{
cout << m_pNodeArray[i].m_cData << " ";
m_pNodeArray[i].m_bIsVisited = true;
curVec.push_back(i);
}
}
}
}
if (curVec.size()==0)
{
return;
}
else
{
breadthFirstTraverseImpl(curVec);
}
}
int CMap::getMinEdge(vector<Edge> edgeVec)
{
int minWeight = 0;
int edgeIndex = 0;
int i = 0;
for (;i<(int)edgeVec.size();i++)
{
if (!edgeVec[i].m_bSelected)
{
minWeight = edgeVec[i].m_iWeightValue;
edgeIndex = 1;
break;
}
}
if (minWeight == 0)
{
return -1;
}
for (;i<(int)edgeVec.size();i++)
{
if (edgeVec[i].m_bSelected)
{
continue;
}
else
{
if (minWeight>edgeVec[i].m_iWeightValue)
{
minWeight = edgeVec[i].m_iWeightValue;
edgeIndex = i;
}
}
}
return edgeIndex;
}
bool CMap::isInSet(vector<int> nodeSet, int target)
{
for (int i = 0;i<(int)nodeSet.size();i++)
{
if (nodeSet[i] == target)
{
return true;
}
}
return false;
}
void CMap::mergeNodeSet(vector<int>& nodeSetA, vector<int> nodeSetB)
{
for (int i = 0; i < (int)nodeSetB.size(); i++)
{
nodeSetA.push_back(nodeSetB[i]);
}
}
【main.cpp】
#include "CMap.h"
int main(void)
{
CMap *pMap = new CMap(6);
Node *pNodeA = new Node('A');
Node *pNodeB = new Node('B');
Node *pNodeC = new Node('C');
Node *pNodeD = new Node('D');
Node *pNodeE = new Node('E');
Node *pNodeF = new Node('F');
pMap->addNode(pNodeA);
pMap->addNode(pNodeB);
pMap->addNode(pNodeC);
pMap->addNode(pNodeD);
pMap->addNode(pNodeE);
pMap->addNode(pNodeF);
pMap->setValueToMatrixForUndirectedGraph(0, 1, 6);
pMap->setValueToMatrixForUndirectedGraph(0, 4, 5);
pMap->setValueToMatrixForUndirectedGraph(0, 5, 1);
pMap->setValueToMatrixForUndirectedGraph(1, 2, 3);
pMap->setValueToMatrixForUndirectedGraph(1, 5, 2);
pMap->setValueToMatrixForUndirectedGraph(2, 5, 8);
pMap->setValueToMatrixForUndirectedGraph(2, 3, 7);
pMap->setValueToMatrixForUndirectedGraph(3, 5, 4);
pMap->setValueToMatrixForUndirectedGraph(3, 4, 2);
pMap->setValueToMatrixForUndirectedGraph(4, 5, 9);
cout <<"普利姆算法" << endl;
pMap->primTree(0);
cout << "克魯斯卡爾算法"<< endl;
pMap->kruskalTree();
return 0;
}
運行結果