16、17讲开始涉及到图,光是伪代码的话实现起来不是很直接,所以干脆补全一下需要用到的图的类定义及一些成员函数,限于篇幅,成员函数、最小堆、并查集等的实现没有贴出来。
graph类定义
template <typename T, typename E>
class Graph
{
public:
Graph(int sz)
{
maxVertices = sz;
numVertices = 0;
numEdges = 0;
}
virtual ~Graph(){};
bool GraphEmpty() const
{ //判图空否
return (numEdges == 0);
}
bool GraphFull() const
{ //判图满否
return (numVertices == maxVertices ||
numEdges == maxVertices*(maxVertices-1)/2);//无向图,有向图不除以2
}
int NumberOfVertices()
{ //返回当前顶点数
return numVertices;
}
int NumberOfEdges()
{ //返回当前边数
return numEdges;
}
void DFS(); //深度优先遍历图,输出所有的连通分量
void BFS(); //广度优先遍历图,输出所有的连通分量
void DFSTree(Tree<T> &tree); //建立立以左子女-右兄弟链表表示的DFS生成森林。
void Components(); //利用深度优先搜索求非连通图的连通分量的算法
void Kruskal(MinSpanTree<T,E> &MST);//最小生成树
void Prim(MinSpanTree<T,E> &MST);
friend istream& operator >> (istream &in, Graph<T,E> &G)
{
int i, j, k, n, m;
T e1, e2;
E weight;
in >> n >> m; //输入顶点数与边数
for (i = 0; i < n; i++)
{ //依次输入顶点值
in >> e1;
G.insertVertex(e1);
}
i = 0;
while (i < m)
{
assert(in >> e1 >> e2 >> weight); //依次输入边的两顶点值及其权值
j = G.getVertexPos(e1); //取对应顶点下标
k = G.getVertexPos(e2);
if (j == -1 || k == -1)
{ //取对应顶点不存在
cout << "Input error!\n";
}
else
{
G.insertEdge(j, k, weight); //由顶点下标和权值插入边
i++;
}
}
return in;
}
friend ostream& operator << (ostream &out, Graph<T,E> &G){
int i, j, n, m;
T e1, e2;
E weight;
n = G.NumberOfVertices(); //顶点数与边数
m = G.NumberOfEdges();
out << "Number of Vertices: " << n << endl;
out << "Number of Edges: " << m << endl;
out << "The edges in the graph are:\n";
for (i = 0; i < n; i++)
{
for (j = i + 1; j < n; j++)
{
weight = G.getWeight(i, j);
if (weight > 0 && weight < maxWeight)
{
e1 = G.getValue(i); //由下标取得顶点值
e2 = G.getValue(j);
out << "(" << e1 << "," << e2 << "," << weight << ")" << endl;
}
}
}
return out;
}
// 图的子类必须实现的一些接口
virtual T getValue(int i) = 0; //取位置为i的顶点中的值
virtual E getWeight(int v1, int v2) = 0; //返回边(v1,v2)上的权值
virtual bool insertVertex(const T &vertex) = 0; //在图中插入一个顶点vertex
virtual bool removeVertex(int v) = 0; //在图中删除一个顶点v
virtual bool insertEdge(int v1, int v2, E weight) = 0; //插入权值为weight的边(v1,v2)
virtual bool removeEdge(int v1, int v2) = 0; //删除边(v1,v2)
virtual int getFirstNeighbor(int v) = 0; //取顶点v的第一个邻接顶点
virtual int getNextNeighbor(int v, int w) = 0; //取v的邻接顶点w的下一邻接顶点
virtual int getVertexPos(const T &vertex) = 0; //给出顶点vertex在图中的位置
protected:
int maxVertices; //图中最大顶点数
int numEdges; //当前边数
int numVertices; //当前顶点数
void DFS(int v, bool visited[]); //深度优先遍历图,子过程
void DFSTree(int v, bool visited[], TreeNode<T> *& subTree);
};
采用邻接链表实现的图的类定义
//邻接表实现的图类
template <typename T, typename E>struct Edge //边结点的定义
{
int dest; //边的另一顶点位置
E cost; //边上的权值
Edge<T,E> *link; //下一条边链指针
Edge() {} //构造函数
Edge(int num, E weight) //构造函数
{
dest = num;
cost = weight;
link = NULL;
}
bool operator != (Edge<T,E> &R)const //判边不等否。#只能用于同一行的单链表中
{
return (dest != R.dest);
}
};
//顶点的定义
template <typename T, typename E>struct Vertex
{
T data; //顶点的名字
Edge<T,E> *adj; //边链表的头指针
};
template <typename T, typename E>class Graphlnk : public Graph<T,E> //图的类定义
{
public:
Graphlnk(int sz = DefaultVertices); //构造函数
~Graphlnk(); //析构函数
T getValue(int i) //返回该邻接顶点的编号,若不存在则返回0
{
return (i >= 0 && i < NumberOfVertices()) ? NodeTable[i].data : 0;
}
E getWeight(int v1, int v2); //返回边(v1,v2)上的权值
bool insertVertex(const T& vertex); //在图中插入一个顶点vertex
bool removeVertex(int v); //在图中删除一个顶点v
bool insertEdge(int v1, int v2, E weight); //在图中插入一条边(v1,v2)
//接受一个参数,表示插入顶点的值,返回true表示插入成功
bool removeEdge(int v1, int v2); //在图中删除一条边(v1,v2)
int getFirstNeighbor(int v); //取顶点v的第一个邻接顶点
//返回第一个邻接定点的编号,若不存在或参数不合理则返回-1
int getNextNeighbor(int v, int w); //取v的邻接顶点w的下一邻接顶点
int getVertexPos(const T &vertex){ //给出顶点vertex在图中的位置
for (int i = 0; i < numVertices; i++){
if (NodeTable[i].data == vertex){
return i;
}
}
return -1;
}
private:
Vertex<T,E> *NodeTable; //顶点表 (各边链表的头结点)
};
MinSpanTree类定义
#ifndef MINSPANTREE_H
#define MINSPANTREE_H
#include <iostream>
#include <cassert>
using namespace std;
const double maxValue = 99999999.0; //机器可表示的、问题中不可能出现的大数
const int DefaultSize2=50;
//最小生成树边结点的类声明
template <typename T, typename E>
struct MSTEdgeNode
{//T为顶点类型,其实在生成树中未用
int tail, head; //两顶点位置
E key; //边上的权值,为结点关键码
MSTEdgeNode()
{ //构造函数
tail = -1;
head = -1;
key = 0;
}
/* MSTEdgeNode<T,E>& operator = (const MSTEdgeNode<T,E> &x)
{//可以用默认
tail=x.tail;
head=x.head;
key=x.key;
return *this;
}*/
friend bool operator < (MSTEdgeNode<T,E> &n1, MSTEdgeNode<T,E> &n2);//类模板的友元函数必须是函数模板
friend bool operator > (MSTEdgeNode<T,E> &n1, MSTEdgeNode<T,E> &n2);//所以必须在这里声明,在类外定义
friend bool operator == (MSTEdgeNode<T,E> &n1, MSTEdgeNode<T,E> &n2);
friend bool operator <= (MSTEdgeNode<T,E> &n1, MSTEdgeNode<T,E> &n2);
};
template <typename T, typename E> bool operator < (MSTEdgeNode<T,E> &n1, MSTEdgeNode<T,E> &n2)
{//只有在类外才能定义为函数模板
return n1.key < n2.key;
}
template <typename T, typename E> bool operator > (MSTEdgeNode<T,E> &n1, MSTEdgeNode<T,E> &n2)
{
return n1.key > n2.key;
}
template <typename T, typename E> bool operator == (MSTEdgeNode<T,E> &n1, MSTEdgeNode<T,E> &n2)
{
return n1.key == n2.key;
}
template <typename T, typename E> bool operator <= (MSTEdgeNode<T,E> &n1, MSTEdgeNode<T,E> &n2)
{
return n1.key <= n2.key;
}
//最小生成树的类定义
template <typename T, typename E>class MinSpanTree
{
protected:
MSTEdgeNode<T,E> *edgevalue; //用边值数组表示树
int maxSize, n; //数组的最大元素个数和当前个数
public:
MinSpanTree(int sz = DefaultSize2 /*- 1*/)
{
maxSize = sz;
n = 0;
edgevalue = new MSTEdgeNode<T,E>[sz];
assert(edgevalue);
}
bool Insert(MSTEdgeNode<T,E> &item); //将边item插入到树中,若树中节点已满,则返回false;
void output(); //自定义函数,顺序输出所有边
};
template<class T,class E>bool MinSpanTree<T,E>::Insert(MSTEdgeNode<T,E> &item){
if(n == maxSize)
{
return false;
}
edgevalue[n] = item;
n++;
return true;
}
template<class T,class E>void MinSpanTree<T,E>::output()
{
for(int i=1; i<=n; i++)
{
cout<<"Edge "<<i<<" : "<<"head = "<<edgevalue[i-1].head<<" ; tail = "
<<edgevalue[i-1].tail<<" ; key = "<<edgevalue[i-1].key<<endl;
}
}
#endif
MST的Kruskal和Prim算法
template <typename T, typename E>
void Graph<T,E>::Kruskal(MinSpanTree<T,E> &MST)
{
MSTEdgeNode<T,E> ed; //边结点辅助单元
int u, v, count;
int n = NumberOfVertices(); //顶点数
int m = NumberOfEdges(); //边数
MinHeap<E,MSTEdgeNode<T,E> > H(m); //最小堆,关键码类型为E
UFSets F(n); //并查集
for (u = 0; u < n; u++)
{
for (v = u+1; v < n; v++)
{
if (getWeight(u,v) != 0) //对应边存在
{
ed.tail = u;
ed.head = v;
ed.key = getWeight(u, v);
H.Insert(ed); //插入堆
}
}
}
count = 1; //最小生成树加入边数计数
while (count < n) //反复执行, 取n-1条边
{
H.RemoveMin(ed); //从最小堆中退出具最小权值的边ed
u = F.Find(ed.tail);
v = F.Find(ed.head); //取两顶点所在集合的根u与v
if (u != v) //不是同一集合, 说明不连通
{
F.SimpleUnion(u, v); //合并, 连通它们
MST.Insert(ed); //该边存入最小生成树
count++;
}
}
}
template <typename T, typename E>
void Graph<T,E>::Prim(MinSpanTree<T, E> &MST)
{
MSTEdgeNode<T, E> ed; //边结点辅助单元
int i, u, v, count;
int n = NumberOfVertices(); //顶点数
int m = NumberOfEdges(); //边数
// int u = getVertexPos(u0); //起始顶点号
u = 0;
MinHeap <E,MSTEdgeNode<T, E> > H(m); //最小堆
bool *Vmst = new bool[n]; //最小生成树顶点集合
for (i = 0; i < n; i++) Vmst[i] = false;
Vmst[u] = true; //u 加入生成树
count = 1;
do
{
v = getFirstNeighbor(u);
while (v != -1) //检测u所有邻接顶点
{
if (!Vmst[v]) //v不在mst中
{
ed.tail = u; ed.head = v;
ed.key = getWeight(u, v);
H.Insert(ed); //(u,v)加入堆
} //堆中存所有u在mst中, v不在mst中的边
v = getNextNeighbor(u, v);
}
while (!H.IsEmpty() && count < n)
{
H.RemoveMin(ed); //选堆中具最小权的边
if (!Vmst[ed.head])
{
MST.Insert(ed); //加入最小生成树
u = ed.head;
Vmst[u] = true; //u加入生成树顶点集合
count++;
break;
}
}
} while (count < n);
}
单源最短路径的Dijkstra算法
template <typename T, typename E>
void ShortestPath(Graph<T,E> &G, T v, E dist[], int path[]){
int n = G.NumberOfVertices();
bool *S = new bool[n]; //最短路径顶点集
int i, j, k; E w, min;
for (i = 0; i < n; i++) {
dist[i] = G.getWeight(v, i);
S[i] = false;
if (i != v && dist[i] < maxValue) path[i] = v;
else path[i] = -1;
}
S[v] = true; dist[v] = 0; //顶点v加入顶点集合
for (i = 0; i < n-1; i++){ //求解各顶点最短路径
min = maxValue;
int u = v; //选不在S中具有最短路径的顶点u
for (j = 0; j < n; j++){
if (!S[j] && dist[j] < min){
u = j;
min = dist[j];
}
}
S[u] = true; //将顶点u加入集合S
for (k = 0; k < n; k++){ //修改
w = G.GetWeight(u, k);
if (!S[k] && w < maxValue && dist[u]+w < dist[k]){ //顶点k未加入S
dist[k] = dist[u]+w;
path[k] = u; //修改到k的最短路径
}
}
}
}