上篇博客中使用Lazy Prim实现的最小生成树算法复杂度为
O
(
E
l
o
g
E
)
O(ElogE)
O(ElogE)
下面使用Prim实现最小生成树,算法复杂度为
O
(
E
l
o
g
V
)
O(ElogV)
O(ElogV)
使用最小索引堆实现Prim
(1)将与节点0相连接的节点的权值放入数组中,节点7的权重最小,那么边0-7属于最小生成树
(2)下面开始考察与节点7相连接的节点,对于节点1来说,数组中没有,直接将0.19放入;对于节点5来说,数组中也没有,直接将0.28放入;对于节点2来说,其权重为0.34,而数组中2的权重为0.26,比0.34小,继续保留,把0.34舍去;对于节点4来说,其权重为0.37,数组中4的权重为0.38,那么将原来的0.38舍去,将0.37放入数组中
(3)将节点7的相连接的节点都选择性的放入数组中后,数组中的最小值为0.19,那么将节点1标红,继续考察节点1相连接的节点,对于节点5,边1-5权重为0.32,比数组中0.28要大,舍弃0.32;对于节点3,权重0.29,数组中没有,将0.29放入数组中;对于节点2,权重0.36,数组中为0.26,将0.36舍弃
(4)按照上述步骤,就可将数组中全部元素都标红,标红的权重的边组成最小生成树
下面是程序实现
程序中的四个txt文件在百度网盘
#include <iostream>
#include <iomanip>
#include <vector>
#include <ctime>
#include <string>
#include "DenseGraph.h"
#include "SparseGraph.h"
#include "ReadGraph.h"
#include "LazyPrimMST.h"
#include "PrimMST.h"
using namespace std;
int main() {
string filename1 = "testG1.txt";
int V1 = 8;
string filename2 = "testG2.txt";
int V2 = 250;
string filename3 = "testG3.txt";
int V3 = 1000;
string filename4 = "testG4.txt";
int V4 = 10000;
SparseGraph<double> g1 = SparseGraph<double>(V1, false);
ReadGraph<SparseGraph<double>, double> readGraph1(g1, filename1);
cout<<filename1<<" load successfully."<<endl;
SparseGraph<double> g2 = SparseGraph<double>(V2, false);
ReadGraph<SparseGraph<double>,double> readGraph2(g2, filename2);
cout<<filename2<<" load successfully."<<endl;
SparseGraph<double> g3 = SparseGraph<double>(V3, false);
ReadGraph<SparseGraph<double>,double> readGraph3(g3, filename3);
cout<<filename3<<" load successfully."<<endl;
SparseGraph<double> g4 = SparseGraph<double>(V4, false);
ReadGraph<SparseGraph<double>,double> readGraph4(g4, filename4);
cout<<filename4<<" load successfully."<<endl;
cout<<endl;
clock_t startTime, endTime;
// Test Lazy Prim MST
cout<<"Test Lazy Prim MST:"<<endl;
startTime = clock();
LazyPrimMST<SparseGraph<double>, double> lazyPrimMST1(g1);
endTime = clock();
cout<<"Test for G1: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
startTime = clock();
LazyPrimMST<SparseGraph<double>, double> lazyPrimMST2(g2);
endTime = clock();
cout<<"Test for G2: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
startTime = clock();
LazyPrimMST<SparseGraph<double>, double> lazyPrimMST3(g3);
endTime = clock();
cout<<"Test for G3: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
startTime = clock();
LazyPrimMST<SparseGraph<double>, double> lazyPrimMST4(g4);
endTime = clock();
cout<<"Test for G4: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
cout<<endl;
// Test Prim MST
cout<<"Test Prim MST:"<<endl;
startTime = clock();
PrimMST<SparseGraph<double>, double> PrimMST1(g1);
endTime = clock();
cout<<"Test for G1: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
startTime = clock();
PrimMST<SparseGraph<double>, double> PrimMST2(g2);
endTime = clock();
cout<<"Test for G2: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
startTime = clock();
PrimMST<SparseGraph<double>, double> PrimMST3(g3);
endTime = clock();
cout<<"Test for G3: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
SparseGraph<double> g5 = SparseGraph<double>(V4, false);
ReadGraph<SparseGraph<double>,double> readGraph5(g5, filename4);
startTime = clock();
PrimMST<SparseGraph<double>, double> PrimMST4(g5);
endTime = clock();
cout<<"Test for G4: "<<(double)(endTime-startTime)/CLOCKS_PER_SEC<<" s."<<endl;
cout<<endl;
return 0;
}
PrimMST.h
#include <iostream>
#include <vector>
#include <cassert>
#include "IndexMinHeap.h"
#ifndef _EDGE_H_
#define _EDGE_H_
#include "Edge.h"
#endif
using namespace std;
template<typename Graph, typename Weight>
class PrimMST{
private:
Graph &G;
IndexMinHeap< Weight > ipq;
vector< Edge<Weight>* > edgeTo;
bool *marked;
vector< Edge<Weight> > mst;
Weight mstWeight;
void visit(int v){
assert( !marked[v] );
marked[v] = true;
typename Graph::adjIterator adj(G,v);
for (Edge<Weight>* e = adj.begin(); !adj.end(); e = adj.next() ){
int w = e->other(v);
if ( !marked[w]){
if ( !edgeTo[w] ){
edgeTo[w] = e;
ipq.insert(w, e->wt());
}
else if ( e->wt() < edgeTo[w]->wt() ){
edgeTo[w] = e;
ipq.change(w, e->wt());
}
}
}
}
public:
// 构造函数, 使用Prim算法求图的最小生成树
PrimMST(Graph &graph):G(graph), ipq(IndexMinHeap<double>(graph.V())){
assert( graph.E() >= 1 );
// 算法初始化
marked = new bool[G.V()];
for( int i = 0 ; i < G.V() ; i ++ ){
marked[i] = false;
edgeTo.push_back(NULL);
}
mst.clear();
// Prim
visit(0);
while( !ipq.isEmpty() ){
// 使用最小索引堆找出已经访问的边中权值最小的边
// 最小索引堆中存储的是点的索引, 通过点的索引找到相对应的边
int v = ipq.extractMinIndex();
assert( edgeTo[v] );
mst.push_back( *edgeTo[v] );
visit( v );
}
mstWeight = mst[0].wt();
for( int i = 1 ; i < mst.size() ; i ++ )
mstWeight += mst[i].wt();
}
~PrimMST(){
delete[] marked;
}
vector< Edge<Weight> > mstEdges(){
return mst;
}
Weight result(){
return mstWeight;
}
};
IndexMinHeap.h
#include <iostream>
#include <algorithm>
#include <cassert>
using namespace std;
template<typename Item>
class IndexMinHeap{
private:
Item *data;
int count;
int capacity;
int *indexes;
int *reverse;
void shiftUp(int k){
while (k > 1 && data[indexes[k/2]] > data[indexes[k]]){
swap(indexes[k/2], indexes[k]);
reverse[indexes[k/2]] = k/2;
reverse[indexes[k]] = k;
k /= 2;
}
}
void shiftDown(int k){
while (k <= count/2){
int j = 2*k; //此轮循环中,data[k]和data[j]交换位置
if (data[indexes[j]] > data[indexes[j+1]] && j + 1 <= count)
j += 1;
if (data[indexes[k]] <= data[indexes[j]])
break;
swap(indexes[j], indexes[k]);
reverse[indexes[j]] = j;
reverse[indexes[k]] = k;
k = j;
}
}
public:
IndexMinHeap(int capacity){
data = new Item[capacity + 1];
indexes = new int[capacity + 1];
reverse = new int[capacity + 1];
for (int i = 0; i <= capacity; i++)
reverse[i] = 0;
count = 0;
this->capacity = capacity;
}
~IndexMinHeap(){
delete[] data;
delete[] indexes;
delete[] reverse;
}
int size(){
return count;
}
bool isEmpty(){
return count == 0;
}
void insert(int index, Item item){
assert( count + 1 <= capacity );
assert( index + 1 >= 1 && index + 1 <= capacity );
index += 1;
data[index] = item;
indexes[count+1] = index;
reverse[index] = count+1;
count++;
shiftUp(count);
}
Item extractMin(){
assert( count > 0 );
Item ret = data[indexes[1]];
swap( indexes[1] , indexes[count] );
reverse[indexes[count]] = 0;
reverse[indexes[1]] = 1;
count--;
shiftDown(1);
return ret;
}
int extractMinIndex(){
assert( count > 0 );
int ret = indexes[1] - 1;
swap( indexes[1] , indexes[count] );
reverse[indexes[count]] = 0;
reverse[indexes[1]] = 1;
count--;
shiftDown(1);
return ret;
}
Item getMin(){
assert( count>0 );
return data[indexes[1]];
}
int getMinIndex(){
assert( count > 0 );
return indexes[1]-1;
}
bool contain( int index ){
return reverse[index+1] != 0;
}
Item getItem( int index ){
assert( contain(index) );
return data[index+1];
}
void change( int index , Item newItem ){
assert( contain(index) );
index += 1;
data[index] = newItem;
shiftUp( reverse[index] );
shiftDown( reverse[index] );
}
};
输出为