/*
Name: 有向图的邻接矩阵类
Copyright: 始发于goal00001111的专栏;允许自由转载,但必须注明作者和出处
Author: goal00001111
Date: 14-01-09 07:49
Description: 本人图论算法学习的一个阶段性总结。
实现了有向图的邻接矩阵类,以及要用到的并查集类,最小生成树类和最小堆类。
实现了有向图的深度优先搜索(包括递归和非递归),广度优先搜索,
最小生成树(包括克鲁斯卡尔算法和普里姆算法),求最短路径的迪克斯特拉算法。
另外还有它的姊妹类《有向图的邻接表类 》:
http://blog.csdn.net/goal00001111/archive/2009/01/14/3772503.aspx
*/
#include <iostream>
#include<vector>
#include<stack>
#include<queue>
#include<string>
#include <time.h>
using namespace std;
const int MaxNumVertices = 50; //最大顶点数
const int MaxNumEdges = 50; //最大边数
const int IBFINITY = INT_MAX; //无穷大
const int NONETITY = -1; //不存在
const int MAX = 50000; //集合的最大成员数量
class UFSet{ //并查集类
public:
UFSet(int s = MAX); //构造函数
UFSet(const UFSet & obj); //拷贝构造函数
~UFSet() //析构函数
{
delete []father;
}
UFSet & operator = (const UFSet & obj); //赋值函数
int FindFather(int pos); //寻找序号pos的族长大人
bool Unite(int posI, int posJ);//将成员posI和posJ合并到同一个家族
int FindFatherAndReducePath(int pos); //查找族长并压缩路径
bool UnionBySize (int posI, int posJ); //按大小求并
bool SameFamily(int posI, int posJ); //判断成员posI和posJ是否属于同一家族
void PrintUFSet(); //输出集合的所有元素
private:
int *father; //并查集成员数组,存放各元素的父亲结点
int size; //并查集成员数量
};
UFSet::UFSet(int s) : size(s) //构造函数
{
father = new int[size + 1];
for (int i=0; i<=size; i++) //所有的数组元素值均初始化为-1
father[i] = -1;
}
UFSet::UFSet(const UFSet & obj) //拷贝构造函数
{
size = obj.size;
father = new int[size + 1];
for (int i=0; i<=size; i++)
father[i] = obj.father[i];
}
UFSet & UFSet::operator = (const UFSet & obj) //赋值函数
{
if (this == &obj) //检查自赋值
return *this;
delete []father; //释放原有的内存资源
size = obj.size; //分配新的内存资源,并复制内容
father = new int[size + 1];
for (int i=0; i<=size; i++)
father[i] = obj.father[i];
return *this; //返回本对象的引用
}
int UFSet::FindFather(int pos)//寻找序号pos的族长大人。若pos本身是族长,则返回自身
{
if (father[pos] < 0)
return pos;
return FindFather(father[pos]);
}
bool UFSet::Unite(int posI, int posJ)//将成员posI和posJ合并到同一个家族
{
//首先各自去寻找自己的族长
int fI = FindFather(posI);
int fJ = FindFather(posJ);
if (fI == fJ) //如果是同一个族长门下,不必合并,即合并失败
return false;
else
father[fJ] = fI; //否则fI当族长:谁让posI站在posJ的前面呢!
return true;
}
int UFSet::FindFatherAndReducePath(int pos)//查找族长并压缩路径
{
if (father[pos] < 0)
return pos;
//若自己不是族长,则找到族长后,将所途经的结点均指向族长
return father[pos] = FindFatherAndReducePath(father[pos]);
}
bool UFSet::UnionBySize(int posI, int posJ)//按大小求并
{
//首先各自去寻找自己的族长
int fI = FindFatherAndReducePath(posI);
int fJ = FindFatherAndReducePath(posJ);
if (fI == fJ) //如果是同一个族长门下,不必合并,即合并失败
return false;
else if (father[fI] < father[fJ])
{//如果族长fI的实力比fJ强,即|fI|>|fJ|,则fI当族长,并修改father[fI]和father[fJ]
father[fI] += father[fJ];
father[fJ] = fI;
}
else //否则fJ当族长
{
father[fJ] += father[fI];
father[fI] = fJ;
}
return true;
}
bool UFSet::SameFamily(int posI, int posJ)//判断成员posI和posJ是否属于同一家族
{
return FindFatherAndReducePath(posI) == FindFatherAndReducePath(posJ);
}
void UFSet::PrintUFSet()//输出集合的所有元素
{
for (int i=0; i<=size; i++)
cout << father[i] << ' ';
cout << endl;
}
///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
template <class DistType>
struct MSTEdgeNode{ //最小生成树边结点定义
int tail, head; //边的两顶点位置
DistType cost; //边的权值
};
template <class DistType>
class MinSpanTree{ //最小生成树类
public:
friend ostream & operator << (ostream & os, const MSTEdgeNode<DistType> & obj);
void Insert(const DistType & item); //插入一条边
void Print(); //输出最小生成树
private:
vector<DistType> edgeValue; //用边值数组表示最小生成树
};
template <class DistType> //插入一条边
void MinSpanTree<DistType>::Insert(const DistType & item)
{
edgeValue.push_back(item);
}
template <class DistType> //输出最小生成树
void MinSpanTree<DistType>::Print()
{
for (int i=0; i<edgeValue.size(); i++)
cout << edgeValue[i] << endl;
cout << endl;
}
//重载输出运算符
template <class DistType>
ostream & operator << (ostream & os, const MSTEdgeNode<DistType> & obj)
{
os << "<" << obj.tail << ", " << obj.head << "> = " << obj.cost;
return os;
}
///////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
template <class DistType>
class MinHeap{ //最小堆类
public:
friend ostream & operator << (ostream & os, const MSTEdgeNode<DistType> & obj);
friend bool operator > (const MSTEdgeNode<DistType> & e1, const MSTEdgeNode<DistType> & e2);
MinHeap() { mH.resize(1); }
MinHeap(const vector<DistType> & vec); //构造函数:将一个普通向量设置成最小堆
DistType GetElement(int pos); //取pos处的元素
void SetElement(int pos, DistType data); //给pos处的元素赋值为data
void RebuildHeap(); //将被打乱的小顶堆重新构造成二叉堆
void PercDown(int pos); //构造二叉堆的功能子函数
DistType GetMin() { return mH[1];}
void InsertHeap(DistType x); //把x插入到二叉最小堆
void DeleteMin();//删除二叉最小堆的根,并通过上移使得新得到的序列仍为二叉最小堆
void Print(); //输出二叉最小堆
private:
vector<DistType> mH; //存储各边值的最小堆
};
template <class DistType> //构造函数:将一个普通向量设置成最小堆
MinHeap<DistType>::MinHeap(const vector<DistType> & vec)
{
mH.resize(1); //第一个元素废弃不用
for (int i=0; i<vec.size(); i++)
{
InsertHeap(vec[i]); //把x插入到二叉最小堆
}
}
template <class DistType> //取pos处的元素
DistType MinHeap<DistType>::GetElement(int pos)
{
return mH[pos];
}
template <class DistType> //给pos处的元素赋值为data
void MinHeap<DistType>::SetElement(int pos, DistType data)
{
mH[pos] = data;
}
template <class DistType> //将被打乱的小顶堆重新构造成二叉堆
void MinHeap<DistType>::RebuildHeap()
{
for (int i=(mH.size()-1)/2; i>0; i--)
{
PercDown(i);
}
}
template <class DistType> //构造二叉堆的功能子函数
void MinHeap<DistType>::PercDown(int pos)
{
DistType min = mH[pos]; //存储需要被调整的元素
int len = mH.size() - 1;
int child;
while (pos * 2 < mH.size())
{
child = pos * 2;
if (child < mH.size()-1 && mH[child] > mH[child+1])
child++;
if (min > mH[child]) //若pos的小儿子小于它,把小儿子移到pos的位置
mH[pos] = mH[child];
else
break;
pos = child;
}
mH[pos] = min;//把二叉最小堆中被调整的元素放到适当的空位,此时得到的序列仍为二叉最小堆
}
template <class DistType> //把x插入到二叉最小堆
void MinHeap<DistType>::InsertHeap(DistType x)
{
mH.push_back(x); //先将x插入到向量尾部
int i;
for (i=mH.size()-1; i/2>0 && mH[i/2]>x; i/=2)//调整x的位置
mH[i] = mH[i/2];
mH[i] = x;
}
//重载条件运算符 >
template <class DistType>
bool operator > (const MSTEdgeNode<DistType> & e1, const MSTEdgeNode<DistType> & e2)
{
return e1.cost > e2.cost;
}
template <class DistType> //删除二叉最小堆的根,并通过上移使得新得到的序列仍为二叉最小堆
void MinHeap<DistType>::DeleteMin()
{
DistType last = mH.back();//存储二叉堆的最后一个元素
mH.pop_back(); //删除最后一个元素
int child, pos = 1;
while (pos * 2 < mH.size()) //把二叉最小堆的某些元素往前移,使得新得到的序列仍为二叉最小堆
{
child = pos * 2;
//若i有右儿子,且右儿子小于左儿子,c指向右儿子
if (child < mH.size()-1 && mH[child] > mH[child+1])
child++;
if (last > mH[child]) //若i的小儿子小于二叉堆的最后一个元素,把其移到i的位置
mH[pos] = mH[child];
else
break;
pos = child;
}
mH[pos] = last; //把二叉最小堆的最后一个元素放到适当的空位,此时得到的序列仍为二叉最小堆
}
template <class DistType> //输出二叉最小堆
void MinHeap<DistType>::Print()
{
for (int i=1; i<mH.size(); i++)
cout << mH[i] << endl;
cout << endl;
}
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
template <class NameType, class DistType>
class Graph{ //有向图的邻接矩阵类
public:
Graph(int sz = 0); //构造函数
Graph(const Graph<NameType, DistType> & obj); //拷贝构造函数
Graph & operator = (const Graph<NameType, DistType> & obj);//赋值函数
bool GraphEmpty() const //空图
{
return verticesVector.empty();
}
bool VerticesFull() const //顶点满了
{
return verticesVector.size() == MaxNumVertices;
}
bool EdgesFull() const //边满了
{
return currentEdges == MaxNumVertices;
}
int NumberOfVertices() //返回当前顶点数
{
return verticesVector.size();
}
int NumberOfEdges() //返回当前边数
{
return currentEdges;
}
NameType GetValue(int i) //取顶点i的值,i不合理则返回空
{
return RightPos(i) ? verticesVector[i] : NULL;
}
int GetVertexPos(const NameType & vertex) //给出顶点vertex在图中的位置
{
for (int i=0; i<verticesVector.size(); i++)
{
if (verticesVector[i] == vertex)
return i;
}
return NONETITY; //若找不到,则返回不存在
}
DistType GetWeight(int v1, int v2); //给出以顶点v1和v2为端点的边上的权值
bool IsNeighbor(int v1, int v2); //判断顶点v2是否为v1的邻接顶点
int GetFirstNeighbor(int v); //给出顶点位置为v的第一个邻接顶点的位置
int GetNextNeighbor(int v1, int v2); //给出顶点位置为v的某邻接顶点v2的下一个邻接顶点的位置
void SetVertex(int v, const NameType & vertex); //给顶点vertex取名字
void InsertVertex(const NameType & vertex); //插入一个顶点vertex,该顶点没有入边
void InsertEdge(int v1, int v2, DistType weight); //插入一个条边(v1,v2),该边的权值为weight
void RemoveVertex(int v); //在图中删去顶点vertex和所有与它相关联的边
void RemoveVertex(NameType v); //在图中删去顶点vertex和所有与它相关联的边
void RemoveEdge(int v1, int v2); //在图中删除边(v1,v2)
void ToUnndirected();//将有向图转化为无向图
void PrintGraph(); //输出图的邻接矩阵
void DFS(int v); //从顶点v位置出发,深度优先搜索主过程
void DFS(vector<bool> & visited, int v); //深度优先搜索子过程
void DFSStack(int v); //从顶点v位置出发,深度优先搜索非递归算法
void BFSQueue(int v); //从顶点v位置出发,广度优先搜索非递归算法
void Kruskal(MinSpanTree<MSTEdgeNode<DistType> > & root); //最小生成树:克鲁斯卡尔算法
void Prim(MinSpanTree<MSTEdgeNode<DistType> > & root, int u); //最小生成树:普里姆算法
void PrimOptimize(MinSpanTree<MSTEdgeNode<DistType> > & root, int u); //普里姆算法优化版
void Dijkstra(MinSpanTree<MSTEdgeNode<DistType> > & root, int u);//求最短路径:迪克斯特拉算法
private:
vector<NameType> verticesVector; //顶点表
DistType Edge[MaxNumVertices][MaxNumVertices]; //邻接矩阵
int currentEdges; //当前边数
bool RightPos(int i)
{
return i >= 0 && i < verticesVector.size();
}
};
template <class NameType, class DistType> //构造函数
Graph<NameType, DistType>::Graph(int sz)
{
NameType name;
for (int i=0; i<sz; i++)
{
cin >> name;
verticesVector.push_back(name); //顶点增1
}
for (int i=0; i<sz; i++) //邻接矩阵初始化:全部未连通
for (int j=0; j<sz; j++)
Edge[i][j] = IBFINITY;
currentEdges = 0; //当前边数初始化
}
template <class NameType, class DistType> //拷贝构造函数
Graph<NameType, DistType>::Graph(const Graph<NameType, DistType> & obj)
{
verticesVector.resize(0); //释放原有的内存资源
for (int i=0; i<obj.verticesVector.size(); i++) //复制顶点信息
verticesVector.push_back(obj.verticesVector[i]);
currentEdges = obj.currentEdges; //复制边信息
for (int i=0; i<obj.verticesVector.size(); i++)
for (int j=0; j<obj.verticesVector.size(); j++)
Edge[i][j] = obj.Edge[i][j]; //给出以顶点i和j为端点的边上的权值
}
template <class NameType, class DistType> //赋值函数
Graph<NameType, DistType> & Graph<NameType, DistType>::operator = (const Graph<NameType, DistType> & obj)
{
if (this == &obj) //检查自赋值
return *this;
verticesVector.resize(0); //释放原有的内存资源
//分配新的内存资源,并复制内容
for (int i=0; i<obj.verticesVector.size(); i++) //复制顶点信息
verticesVector.push_back(obj.verticesVector[i]);
currentEdges = obj.currentEdges; //复制边信息
for (int i=0; i<obj.verticesVector.size(); i++)
for (int j=0; j<obj.verticesVector.size(); j++)
Edge[i][j] = obj.Edge[i][j]; //给出以顶点i和j为端点的边上的权值
return *this; //返回本对象的引用
}
template <class NameType, class DistType> //给出以顶点v1和v2为端点的边上的权值
DistType Graph<NameType, DistType>::GetWeight(int v1, int v2)
{
if (RightPos(v1) && RightPos(v2))
return Edge[v1][v2];
else
return NONETITY; //无权值,则返回不存在
}
template <class NameType, class DistType> //判断顶点v2是否为v1的邻接顶点
bool Graph<NameType, DistType>::IsNeighbor(int v1, int v2)
{
if (RightPos(v1) && RightPos(v2))
return (Edge[v1][v2] >= 0 && Edge[v1][v2] < IBFINITY);
else
return false; //否则返回假
}
template <class NameType, class DistType> //给出顶点位置为v的第一个邻接顶点的位置
int Graph<NameType, DistType>::GetFirstNeighbor(int v)
{
if (RightPos(v))
{
for (int col=0; col<verticesVector.size(); col++)
{
if (Edge[v][col] >= 0 && Edge[v][col] < IBFINITY)
return col;
}
}
return NONETITY; //若找不到,则返回不存在
}
template <class NameType, class DistType> //给出顶点位置为v1的某邻接顶点v2的下一个邻接顶点的位置
int Graph<NameType, DistType>::GetNextNeighbor(int v1, int v2)
{
if (RightPos(v1) && RightPos(v2))
{
for (int col=v2+1; col<verticesVector.size(); col++)
{
if (Edge[v1][col] >= 0 && Edge[v1][col] < IBFINITY)
return col;
}
}
return NONETITY; //若找不到,则返回不存在
}
template <class NameType, class DistType> //给顶点vertex取名字
void Graph<NameType, DistType>::SetVertex(int v, const NameType & vertex)
{
if (RightPos(v))
{
verticesVector[v] = vertex;
}
}
template <class NameType, class DistType> //插入一个顶点vertex,该顶点没有出入边
void Graph<NameType, DistType>::InsertVertex(const NameType & vertex)
{
if (VerticesFull())
{
cout << "Vertices Are Full" << endl;
return ;
}
for (int col=0; col<=verticesVector.size(); col++)//加一行
Edge[verticesVector.size()][col] = IBFINITY;
for (int row=0; row<=verticesVector.size(); row++)//加一列
Edge[row][verticesVector.size()] = IBFINITY;
verticesVector.push_back(vertex); //顶点增1
}
template <class NameType, class DistType> //插入一个条边<v1,v2>,该边的权值为weight
void Graph<NameType, DistType>::InsertEdge(int v1, int v2, DistType weight)
{
if (EdgesFull())
{
cout << "Edges Are Full" << endl;
return ;
}
if (RightPos(v1) && RightPos(v2))
{
Edge[v1][v2] = weight;
currentEdges++;
}
}
template <class NameType, class DistType> //将有向图转化为无向图:取小权值的边
void Graph<NameType, DistType>::ToUnndirected()
{
for (int row=0; row<=verticesVector.size(); row++)
{
for (int col=0; col<=verticesVector.size(); col++)
{
if (Edge[row][col] > Edge[col][row])
Edge[row][col] = Edge[col][row];
else
Edge[col][row] = Edge[row][col];
}
}
}
template <class NameType, class DistType> //在图中删除边(v1,v2)
void Graph<NameType, DistType>::RemoveEdge(int v1, int v2)
{
if (GraphEmpty())
{
cout << "Graph Is Empty" << endl;
return ;
}
if (RightPos(v1) && RightPos(v2))
{
Edge[v1][v2] = IBFINITY;
currentEdges--;
}
}
template <class NameType, class DistType> //在图中删去顶点vertex和所有与它相关联的边
void Graph<NameType, DistType>::RemoveVertex(int v)
{
if (GraphEmpty())
{
cout << "Graph Is Empty" << endl;
return ;
}
if (!RightPos(v))
return ;
for (int col=0; col<verticesVector.size(); col++)//删除第v行
{
if (Edge[v][col] != IBFINITY) //删除边
currentEdges--;
for (int i=v; i<verticesVector.size()-1; i++)
Edge[i][col] = Edge[i+1][col];
}
for (int row=0; row<verticesVector.size(); row++)//删除第v列
{
for (int i=v; i<verticesVector.size()-1; i++)
Edge[row][i] = Edge[row][i+1];
}
verticesVector.erase(verticesVector.begin()+v); //删除顶点
}
template <class NameType, class DistType> //在图中删去顶点vertex和所有与它相关联的边
void Graph<NameType, DistType>::RemoveVertex(NameType v)
{
RemoveVertex(GetVertexPos(v));
}
template <class NameType, class DistType> //输出图的邻接矩阵
void Graph<NameType, DistType>::PrintGraph()
{
for (int row=0; row<verticesVector.size(); row++)
{
for (int col=0; col<verticesVector.size(); col++)
{
if (Edge[row][col] == IBFINITY)
cout << "&" << ' ';
else
cout << Edge[row][col] << ' ';
}
cout << endl;
}
}
template <class NameType, class DistType> //从顶点v位置出发,深度优先搜索主过程
void Graph<NameType, DistType>::DFS(int v)
{
vector<bool> visited(verticesVector.size(), false); //创建辅助数组并初始化
DFS(visited, v); //从顶点0开始深度优先搜索
}
template <class NameType, class DistType> //深度优先搜索子过程
void Graph<NameType, DistType>::DFS(vector<bool> & visited, int v)
{
cout << GetValue(v) << ' '; //访问该顶点
visited[v] = true; //标记该顶点已经被访问过
int w = GetFirstNeighbor(v); //给出顶点位置为v的第一个邻接顶点的位置
while (w != NONETITY)
{
if (!visited[w]) //若顶点w未访问过,从w递归访问
DFS(visited, w);
w = GetNextNeighbor(v, w); //给出顶点位置为v的某邻接顶点v2的下一个邻接顶点的位置
}
}
template <class NameType, class DistType> //从顶点v位置出发,深度优先搜索非递归算法
void Graph<NameType, DistType>::DFSStack(int v)
{
vector<bool> visited(verticesVector.size(), false); //创建辅助数组并初始化
stack<int> s; //设置一个栈用来存储各个结点
cout << GetValue(v) << ' '; //访问第一个顶点
visited[v] = true; //标记该顶点已经被访问过
s.push(v); //第一个顶点入栈
int w = GetFirstNeighbor(v); //用来标记v的当前邻接顶点
while (s.size() > 1 || w != NONETITY) //有邻接顶点或栈非空
{
if (w != NONETITY) //有邻接顶点
{
if (!visited[w])//若顶点w未访问过,访问该顶点w,并继续寻找w的第一个邻接顶点
{
v = w;
cout << GetValue(v) << ' '; //访问该顶点
visited[v] = true; //标记该顶点已经被访问过
s.push(v); //将访问过的顶点入栈
w = GetFirstNeighbor(v);
}
else //否则寻找v的邻接顶点w的下一个邻接顶点的位置
w = GetNextNeighbor(v, w);
}
else //退栈
{
w = s.top(); //定位上次的邻接顶点
s.pop();
v = s.top();
w = GetNextNeighbor(v, w); //寻找v的邻接顶点w的下一个邻接顶点的位置
}
}
}
template <class NameType, class DistType> //从顶点v位置出发,广度优先搜索非递归算法
void Graph<NameType, DistType>::BFSQueue(int v)
{
vector<bool> visited(verticesVector.size(), false); //创建辅助数组并初始化
queue<int> q; //q是实现分层访问的队列
cout << GetValue(v) << ' '; //访问第一个顶点
visited[v] = true; //标记该顶点已经被访问过
q.push(v); //第一个顶点入队
while (!q.empty())
{
v = q.front(); //从队列中退出顶点v
q.pop();
int w = GetFirstNeighbor(v); //寻找v的第一个邻接顶点
while (w != NONETITY)
{
if (!visited[w])//若顶点w未访问过,访问该顶点w
{
cout << GetValue(w) << ' '; //访问该顶点
visited[w] = true; //标记该顶点已经被访问过
q.push(w); //将访问过的顶点入队
}
//否则寻找v的邻接顶点w的下一个邻接顶点的位置
w = GetNextNeighbor(v, w);
}
}
}
/*
克鲁斯卡尔算法
假设 WN=(V,{E}) 是一个含有 n 个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:
先构造一个只含 n 个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,
则它是一个含有 n 棵树的一个森林。之后,从网的边集 E 中选取一条权值最小的边,
若该条边的两个顶点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;
反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。
依次类推,直至森林中只有一棵树,也即子图中含有 n-1条边为止。
*/
template <class NameType, class DistType> //最小生成树:克鲁斯卡尔算法
void Graph<NameType, DistType>::Kruskal(MinSpanTree<MSTEdgeNode<DistType> > & root)
{
UFSet ufS(NumberOfVertices()); //连通分量
MSTEdgeNode<DistType> e;
MinHeap<MSTEdgeNode<DistType> > minHeap; //存储各边值的最小堆
//构造一个二叉堆;小顶堆
for (int row=0; row<verticesVector.size(); row++)
{
for (int col=0; col<verticesVector.size(); col++)
{
if (Edge[row][col] != IBFINITY)
{
e.tail = row;
e.head = col;
e.cost = Edge[row][col];
minHeap.InsertHeap(e); //把node插入到二叉堆
}
}
}
int count = 1; //最小生成树边计数
while (count < NumberOfVertices())//反复执行取 NumberOfVertices()-1条边
{
e = minHeap.GetMin(); //获取最小权值的边
minHeap.DeleteMin();//从最小堆中退出具有最小权值的边
if (!ufS.SameFamily(e.tail, e.head))//判断两点是否在同一集合
{
ufS.UnionBySize(e.tail, e.head); //两点不在同一集合,合并连通它们
root.Insert(e); //该边加入最小生成树
count++;
}
}
}
/*
普里姆算法
假设 WN=(V,{E})是一个含有n个顶点的连通网,TV是 WN上最小生成树中顶点的集合,TE是最小生成树中边的集合。
显然,在算法执行结束时,TV=V,而 TE 是 E 的一个子集。在算法开始执行时,TE 为空集,TV 中只有一个顶点,
因此,按普里姆算法构造最小生成树的过程为:在所有“其一个顶点已经落在生成树上,
而另一个顶点尚未落在生成树上”的边中取一条权值为最小的边,逐条加在生成树上,
直至生成树中含有 n-1条边为止。
*/
template <class NameType, class DistType> //最小生成树:普里姆算法
void Graph<NameType, DistType>::Prim(MinSpanTree<MSTEdgeNode<DistType> > & root, int u)
{ //记录生成树顶点集合外各顶点集合内哪个顶点最近(即权值最小)
vector<int> nearVex(NumberOfVertices(), u);
nearVex[u] = NONETITY; //顶点u加到生成树顶点集合,用NONETITY表示
//存放生成树顶点集合内顶点到生成树外各顶点的边上的当前最小权值
vector<DistType> lowCost(NumberOfVertices());
for (int col=0; col<verticesVector.size(); col++)
lowCost[col] = Edge[u][col];
MSTEdgeNode<DistType> e;
for (int i=1; i<NumberOfVertices(); i++) //反复执行取 NumberOfVertices()-1条边
{
DistType min = IBFINITY; //取最大权值,实际权值都小于IBFINITY
int v = u;
for (int j=0; j<NumberOfVertices(); j++)//确定当前具有最小权值的边及顶点位置
{
if (nearVex[j] != NONETITY && lowCost[j] < min)
{
v = j;
min = lowCost[j];
}
}
if (v != u) //v == u表示再也找不到满足要求的顶点了,即不指向任何一个顶点
{
e.tail = nearVex[v];
e.head = v;
e.cost = lowCost[v];
root.Insert(e); //该边加入最小生成树
nearVex[v] = NONETITY; //加入生成树顶点集合
//修改nearVex[j] : lowCost[j] = min{lowCost[j], Edge[v][j]}
for (int j=0; j<NumberOfVertices(); j++)
{
if (nearVex[j] != NONETITY && Edge[v][j] < lowCost[j])
{
lowCost[j] = Edge[v][j];
nearVex[j] = v;
}
}
}
}
}
template <class NameType, class DistType> //最小生成树:普里姆算法优化版
void Graph<NameType, DistType>::PrimOptimize(MinSpanTree<MSTEdgeNode<DistType> > & root, int u)
{ //记录生成树顶点集合外各顶点集合内哪个顶点最近(即权值最小)
vector<int> nearVex(NumberOfVertices(), u);
nearVex[u] = NONETITY; //顶点u加到生成树顶点集合,用NONETITY表示
//存放生成树顶点集合内顶点到生成树外各顶点的边上的当前最小权值
vector<DistType> lowCost(NumberOfVertices());
for (int col=0; col<verticesVector.size(); col++)
lowCost[col] = Edge[u][col];
MSTEdgeNode<DistType> e;
for (int i=1; i<NumberOfVertices(); i++) //反复执行取 NumberOfVertices()-1条边
{
DistType min = IBFINITY; //取最大权值,实际权值都小于IBFINITY
int v = u;
for (int j=0; j<NumberOfVertices(); j++)//确定当前具有最小权值的边及顶点位置
{
if (nearVex[j] != NONETITY && lowCost[j] < min)
{
v = j;
min = lowCost[j];
}
}
if (v != u) //v == u表示再也找不到满足要求的顶点了,即不指向任何一个顶点
{
e.tail = nearVex[v];
e.head = v;
e.cost = lowCost[v];
root.Insert(e); //该边加入最小生成树
nearVex[v] = NONETITY; //加入生成树顶点集合
//修改nearVex[j] : lowCost[j] = min{lowCost[j], Edge[v][j]}
for (int j=0; j<NumberOfVertices(); j++)
{
if (nearVex[j] != NONETITY && Edge[v][j] < lowCost[j])
{
lowCost[j] = Edge[v][j];
nearVex[j] = v;
}
}
}
}
}
template <class NameType, class DistType> //求最短路径:迪克斯特拉算法
void Graph<NameType, DistType>::Dijkstra(MinSpanTree<MSTEdgeNode<DistType> > & root, int u)
{
vector<bool> pass(NumberOfVertices(), false);//存放各顶点是否已经访问的信息
vector<int> path(NumberOfVertices()); //存放在最短路径上该顶点的前一顶点的顶点号
vector<DistType> lowCost(NumberOfVertices()); //存放从顶点u到其它各顶点的最短路径长度
for (int col=0; col<verticesVector.size(); col++)
{
lowCost[col] = Edge[u][col];
if (lowCost[col] < IBFINITY)
path[col] = u;
else
path[col] = NONETITY;
}
pass[u] = true; //顶点u加入顶点集合
lowCost[u] = 0;
MSTEdgeNode<DistType> e;
for (int i=1; i<NumberOfVertices(); i++) //反复执行取 NumberOfVertices()-1条边
{
DistType min = IBFINITY; //取最大权值,实际权值都小于IBFINITY
int v = u;
for (int j=0; j<NumberOfVertices(); j++)//确定当前具有最小权值的边及顶点位置
{
if (!pass[j] && lowCost[j] < min)
{
v = j;
min = lowCost[j];
}
}
pass[v] = true; //顶点v加入顶点集合,表示它已经在最短路径上
e.tail = path[v];
e.head = v;
e.cost = lowCost[v];
root.Insert(e); //该边加入最小生成树
//修改path[j] : lowCost[j] = min{lowCost[j], Edge[v][j]}
for (int j=0; j<NumberOfVertices(); j++)
{
if (!pass[j] && Edge[v][j] < IBFINITY && lowCost[v]+Edge[v][j] < lowCost[j])
{
lowCost[j] = lowCost[v] + Edge[v][j];
path[j] = v;
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
int main()
{
Graph<string, int> obj(6);
//插入一个条边(v1,v2),该边的权值为weight
for (int i=0; i<50; i++)
{
int v1 = rand()%obj.NumberOfVertices();
int v2 = rand()%obj.NumberOfVertices();
if (v1 == v2)
continue;
obj.InsertEdge(v1, v2, rand()%9 + 1);
}
obj.PrintGraph();cout << endl;
cout << "InsertVertex(""H"")" << endl;
obj.InsertVertex("H"); //插入一个顶点vertex,该顶点没有入边
cout << "InsertEdge(1, 0, 8)" << endl;
obj.InsertEdge(1, 0, 8); //插入一个条边(v1,v2),该边的权值为weight
cout << "当前顶点数" << obj.NumberOfVertices() << endl; //返回当前顶点数
for (int i=0; i<obj.NumberOfVertices(); i++)
cout << obj.GetValue(i) << ' ';
cout << endl;
obj.PrintGraph();cout << endl;
cout << "RemoveEdge(3, 4)" << endl;
obj.RemoveEdge(3, 4); //在图中删除边(v1,v2)
obj.PrintGraph();cout << endl;
cout << "RemoveVertex(2)" << endl;
obj.RemoveVertex(2); //在图中删去顶点vertex和所有与它相关联的边
cout << "当前顶点数" << obj.NumberOfVertices() << endl; //返回当前顶点数
for (int i=0; i<obj.NumberOfVertices(); i++)
cout << obj.GetValue(i) << ' ';
cout << endl;
obj.PrintGraph();cout << endl;
cout << "RemoveVertex(""H"")" << endl;
obj.RemoveVertex("H");
cout << "当前顶点数" << obj.NumberOfVertices() << endl; //返回当前顶点数
for (int i=0; i<obj.NumberOfVertices(); i++)
cout << obj.GetValue(i) << ' ';
cout << endl;
obj.PrintGraph();cout << endl;
cout << "深度优先搜索:" << endl;
obj.DFS(0);
cout << endl;
cout << "深度优先搜索:" << endl;
obj.DFSStack(0);
cout << endl;
cout << "广度优先搜索:" << endl;
obj.BFSQueue(1); //从顶点v位置出发,广度优先搜索非递归算法
cout << endl;
obj.ToUnndirected();//将有向图转化为无向图
obj.PrintGraph();cout << endl;
cout << "最小生成树:克鲁斯卡尔算法:" << endl;
MinSpanTree<MSTEdgeNode<int> > root_1; //root是带权无向图的边集合
obj.Kruskal(root_1); //最小生成树:克鲁斯卡尔算法
root_1.Print();
cout << "最小生成树:普里姆算法:" << endl;
for (int i=0; i<obj.NumberOfVertices(); i++)
{
MinSpanTree<MSTEdgeNode<int> > root_2; //root是带权无向图的边集合
obj.Prim(root_2, i); //最小生成树:普里姆算法
root_2.Print();
}
cout << "求最短路径:迪克斯特拉算法:" << endl;
for (int i=0; i<obj.NumberOfVertices(); i++)
{
MinSpanTree<MSTEdgeNode<int> > root_2;
obj.Dijkstra(root_2, i); //求最短路径:迪克斯特拉算法
root_2.Print();
}
Graph<string, int> gra(obj);
gra = obj;
obj.PrintGraph();cout << endl;
gra.PrintGraph();cout << endl;
cout << endl;
Graph<string, int> gra2;
gra2 = gra;
gra2.PrintGraph();cout << endl;
cout << endl;
system("pause");
return 0;
}