- 在下圖標出的城市間架設一條通訊線路
要求:ios
- 任意兩個城市間都可以通訊
- 將假設成本降至最低
問題:如何在圖中選擇 n-1 條邊使得 n 個頂點間兩兩可達,而且這 n-1 條邊的權值之和最小?
- 僅使用圖中的 n-1 條邊鏈接圖中的 n 個頂點
- 不能使用產生迴路的邊
- 各邊上的權值的總和達到最小
- 選擇某一頂點 v
0
做爲起始頂點,使得 T = {v0
}, F={v1
,v2
,...,vn
}, E={}- 每次選擇一條邊,這條邊是全部 (u, v) 中權值最小的邊,且 u ∈ T, v ∈ F
- 修改 T, F, E :
T = T + {v}, F = F - {v}, E = E + {(u,v)}
- 當 F != NULL 時,且 (u, v) 存在,轉2; 不然結束
類型 | 變量 | 用途 |
Array<bool> | mark | 標記頂點所屬的集合 |
Array[E] | cost | 記錄T集合到F集合中頂點的最小權值 |
Array<int> | adjVex | 記錄 cost 中權值的對應頂點 |
Queue<Edge> | ret | 記錄最小生成樹中的邊 |
注: 如何 T 集合到 F 集合中同一個頂點的鏈接有多條邊,那麼選擇權值最小的鏈接
步驟描述:
- 最小生成樹僅針對無向圖有意義
- 必須判斷圖對象是否可以看做無向圖
問題:什麼樣的有向圖可以看做無向圖
有向圖的任意兩頂點若存在鏈接,則兩點需互相可達且權值相等算法
virtual bool isAdjacent(int i, int j) = 0;編程
- 判斷在當前圖中頂點 i 到頂點 j 是否鄰接
bool asUndirected();ide
- 判斷當前的有向圖是否可以看做無向圖
// 鄰接矩陣圖結構 bool isAdjacent(int i, int j) const override { return (0 <= i) && (i < vCount()) && (0 <= j) && (j < vCount()) && (m_edges[i][j] != nullptr); } // 鄰接鏈表圖結構 bool isAdjacent(int i, int j) const override { return (0 <= i) && (i < vCount()) && (0 <= j) && (j < vCount()) && (m_list.get(i)->edge.find(Edge<E>(i, j)) >=0); } bool asUndirected() { bool ret = true; for (int i=0; i<vCount() && ret; ++i) { for (int j=0; j<vCount() && ret; ++j) { if (isAdjacent(i, j)) { ret = isAdjacent(j, i) && (getEdge(i, j) == getEdge(j, i)); } } } return ret; }
文件:Graph.h函數
#ifndef GRAPH_H #define GRAPH_H #include "Object.h" #include "SharedPointer.h" #include "DynamicArray.h" #include "LinkQueue.h" #include "LinkStack.h" namespace DTLib { template <typename E> struct Edge : public Object { int b; int e; E data; Edge(int i=-1, int j=-1) : b(i), e(j) { } Edge(int i, int j, const E &value) : b(i), e(j), data(value) { } bool operator == (const Edge &obj) { return (b == obj.b) && (e == obj.e); } bool operator != (const Edge &obj) { return !(*this == obj); } }; template <typename V, typename E> class Graph : public Object { public: virtual V getVertex(int i) const = 0; virtual bool getVertex(int i, V &value) const = 0; virtual bool setVertex(int i, const V &value) = 0; virtual SharedPointer<Array<int>> getAdjacent(int i) const = 0; virtual bool isAdjacent(int i, int j) const = 0; virtual E getEdge(int i, int j) const = 0; virtual bool getEdge(int i, int j, E &value) const = 0; virtual bool setEdge(int i, int j, const E &value) = 0; virtual bool removeEdge(int i, int j) = 0; virtual int vCount() const = 0; virtual int eCount() = 0; virtual int OD(int i) = 0; virtual int ID(int i) = 0; virtual int TD(int i) { return OD(i) + ID(i); } bool asUndirected() { bool ret = true; for (int i=0; i<vCount() && ret; ++i) { for (int j=0; j<vCount() && ret; ++j) { if (isAdjacent(i, j)) { ret = isAdjacent(j, i) && (getEdge(i, j) == getEdge(j, i)); } } } return ret; } SharedPointer<Array<int>> BFS(int i) { DynamicArray<int> *ret = nullptr; if ((0 <= i) && (i < vCount())) { LinkQueue<int> q; LinkQueue<int> r; DynamicArray<bool> visited(vCount()); for (int j=0; j<visited.length(); ++j) { visited[j] = false; } q.add(i); while (q.length() > 0) { int v = q.front(); q.remove(); if (!visited[v]) { SharedPointer<Array<int>> aj = getAdjacent(v); for (int j=0; j<aj->length(); ++j) { q.add((*aj)[j]); } r.add(v); visited[v] = true; } } ret = toArray(r); } else { THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is invalid ..."); } return ret; } #ifdef DFS_R SharedPointer<Array<int>> DFS(int i) // 遞歸版深度優先遍歷 { DynamicArray<int> *ret = nullptr; if ((0 <= i) && (i < vCount())) { LinkQueue<int> r; DynamicArray<bool> visited(vCount()); for (int j=0; j<vCount(); ++j) { visited[j] = false; } DFP(i, visited, r); ret = toArray(r); } else { THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is invalid ..."); } return ret; } #else SharedPointer<Array<int>> DFS(int i) { DynamicArray<int> *ret = nullptr; if ((0 <= i) && (i < vCount())) { LinkStack<int> s; LinkQueue<int> r; DynamicArray<bool> visited(vCount()); for (int j=0; j<visited.length(); ++j) { visited[j] = false; } s.push(i); while (s.size() > 0) { int v = s.top(); s.pop(); if (!visited[v]) { SharedPointer<Array<int>> aj = getAdjacent(v); for (int j=aj->length()-1; j>=0; --j) { s.push((*aj)[j]); } r.add(v); visited[v] = true; } } ret = toArray(r); } else { THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is invalid ..."); } return ret; } #endif SharedPointer<Array<Edge<E>>> prim(const E &LIMIT, bool MINIMUM = true) { LinkQueue<Edge<E>> ret; if (asUndirected()) { DynamicArray<int> adjVex(vCount()); DynamicArray<bool> mark(vCount()); DynamicArray<E> cost(vCount()); SharedPointer<Array<int>> aj = nullptr; bool end = false; int v = 0; for (int i=0; i<vCount(); ++i) { adjVex[i] = -1; mark[i] = false; cost[i] = LIMIT; } mark[v] = true; aj = getAdjacent(v); for (int i=0; i<aj->length(); ++i) { cost[(*aj)[i]] = getEdge(v, (*aj)[i]); adjVex[(*aj)[i]] = v; } for (int i=0; i<vCount() && !end; ++i) { E m = LIMIT; int k = -1; for (int j=0; j<vCount(); ++j) { if (!mark[j] && (MINIMUM ? (m > cost[j]) : (m < cost[j]))) { m = cost[j]; k = j; } } end = (k == -1); if (!end) { ret.add(Edge<E>(adjVex[k],k, getEdge(adjVex[k],k))); mark[k] = true; aj = getAdjacent(k); for (int j=0; j<aj->length(); ++j) { if (!mark[(*aj)[j]] && (MINIMUM ? (getEdge(k, (*aj)[j]) < cost[(*aj)[j]]) : (getEdge(k, (*aj)[j]) > cost[(*aj)[j]]))) { cost[(*aj)[j]] = getEdge(k, (*aj)[j]); adjVex[(*aj)[j]] = k; } } } } } else { THROW_EXCEPTION(InvalidOpertionExcetion, "Prim operator is for undirected grap only ..."); } if (ret.length() != (vCount() - 1)) { THROW_EXCEPTION(InvalidOpertionExcetion, "No enough edge for prim operation ..."); } return toArray(ret); } protected: template <typename T> DynamicArray<T>* toArray(LinkQueue<T> &queue) { DynamicArray<T> *ret = new DynamicArray<T>(queue.length()); if (ret != nullptr) { for (int i=0; i<ret->length(); ++i, queue.remove()) { ret->set(i, queue.front()); } } else { THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create ret obj ..."); } return ret; } #ifdef DFS_R void DFP(int i, DynamicArray<bool> &visited, LinkQueue<int>& queue) { if (!visited[i]) { queue.add(i); visited[i] = true; SharedPointer<Array<int>> aj = getAdjacent(i); for (int j=0; j<aj->length(); ++j) { DFP((*aj)[j], visited, queue); } } } #endif }; } #endif // GRAPH_H
文件:main.cppthis
#include <iostream> #include "MatrixGraph.h" #include "ListGraph.h" using namespace std; using namespace DTLib; template< typename V, typename E > Graph<V, E>& GraphEasy() { static MatrixGraph<4, V, E> g; g.setEdge(0, 1, 1); g.setEdge(1, 0, 1); g.setEdge(0, 2, 3); g.setEdge(2, 0, 3); g.setEdge(1, 2, 1); g.setEdge(2, 1, 1); g.setEdge(1, 3, 4); g.setEdge(3, 1, 4); g.setEdge(2, 3, 1); g.setEdge(3, 2, 1); return g; } template< typename V, typename E > Graph<V, E>& GraphComplex() { static ListGraph<V, E> g(9); g.setEdge(0, 1, 10); g.setEdge(1, 0, 10); g.setEdge(0, 5, 11); g.setEdge(5, 0, 11); g.setEdge(1, 2, 18); g.setEdge(2, 1, 18); g.setEdge(1, 8, 12); g.setEdge(8, 1, 12); g.setEdge(1, 6, 16); g.setEdge(6, 1, 16); g.setEdge(2, 3, 22); g.setEdge(3, 2, 22); g.setEdge(2, 8, 8); g.setEdge(8, 2, 8); g.setEdge(3, 8, 21); g.setEdge(8, 3, 21); g.setEdge(3, 6, 24); g.setEdge(6, 3, 24); g.setEdge(3, 7, 16); g.setEdge(7, 3, 16); g.setEdge(3, 4, 20); g.setEdge(4, 3, 20); g.setEdge(4, 5, 26); g.setEdge(5, 4, 26); g.setEdge(4, 7, 7); g.setEdge(7, 4, 7); g.setEdge(5, 6, 17); g.setEdge(6, 5, 17); g.setEdge(6, 7, 19); g.setEdge(7, 6, 19); return g; } void func1() { cout << "func1: ---------------------" << endl; Graph<int, int>& g = GraphEasy<int, int>(); SharedPointer< Array< Edge<int> > > sa = g.prim(65535); int w = 0; for(int i=0; i<sa->length(); i++) { w += (*sa)[i].data; cout << (*sa)[i].b << " " << (*sa)[i].e << " " << (*sa)[i].data << endl; } cout << "Weight: " << w << endl; } void func2() { cout << "func2: ---------------------" << endl; Graph<int, int>& g = GraphComplex<int, int>(); SharedPointer< Array< Edge<int> > > sa = g.prim(65535); int w = 0; for(int i=0; i<sa->length(); i++) { w += (*sa)[i].data; cout << (*sa)[i].b << " " << (*sa)[i].e << " " << (*sa)[i].data << endl; } cout << "Weight: " << w << endl; } void func3() { cout << "func3: ---------------------" << endl; Graph<int, int>& g = GraphComplex<int, int>(); SharedPointer< Array< Edge<int> > > sa = g.prim(0, false); int w = 0; for(int i=0; i<sa->length(); i++) { w += (*sa)[i].data; cout << (*sa)[i].b << " " << (*sa)[i].e << " " << (*sa)[i].data << endl; } cout << "Weight: " << w << endl; } int main() { func1(); func2(); func3(); return 0; }
輸出:spa
func1: --------------------- 0 1 1 1 2 1 2 3 1 Weight: 3 func2: --------------------- 0 1 10 0 5 11 1 8 12 8 2 8 1 6 16 6 7 19 7 4 7 7 3 16 Weight: 99 func3: --------------------- 0 5 11 5 4 26 4 3 20 3 6 24 3 2 22 3 8 21 6 7 19 2 1 18 Weight: 161
- 最小生成樹使得頂點間的連通代價最小
- Prim 算法經過頂點的動態標記尋找最小生成樹
- Prim 算法的關鍵是集合概念的運用 (T集合, F集合)
- 利用 Prim 算法的思想也能尋找圖的 "最大生成樹"
以上內容整理於狄泰軟件學院系列課程,請你們保護原創!3d