1. 图类
#ifndef GRAPH_H
#define GRAPH_H
const int maxvertex=100; //最多可以有100个顶点
const int maxedge=1000; //最多可以有1000条边
const double maxweight=1e10; //权值最大值
#include <iostream>
#include <cstdlib>
#include <memory>
using namespace std;
template<class ElemType>
class Graph {
private:
bool IsDirect; //是否是有向图,false表示无向图
bool IsWeight; //是否带权图
protected:
typedef struct lnode { //邻接链表结点
int sequence; //顶点的序号0,1,2,...
struct lnode *next;
};
typedef struct tnode { //表结点
ElemType vertex; //顶点的值
lnode *link;
};
typedef struct Graph_struct {
tnode *array;
double **connect; //邻接矩阵,矩阵元素是边上的权值
int edgenum; //图的边数
int vertexnum; //图的顶点数
};
Graph_struct graph;
bool *visited; //记录顶点是否被访问过
void DFSTranverse(int start);
void Get(int index,ElemType& e); //根据索引号获取顶点
public:
Graph(bool IsDirect,bool IsWeight);
void DFS();
void show();
virtual ~Graph();
};
#endif
以上定义既包括了图的邻接链表也包括了图的邻接矩阵,既适合有向图也适合无向图,既适合有权图,也适合无权图。
2. 图类实现
#include "Graph.h"
template<class ElemType>
Graph<ElemType>::Graph(bool IsDirect,bool IsWeight) {
this->IsDirect=IsDirect;
this->IsWeight=IsWeight;
int n;
int i,j;
cout<<"请输入图的顶点数:";
cin>>n;
if(!cin.good()) {
cerr<<"输入参数格式不合法!"<<endl;
return;
}
if(n<=0 || n>maxvertex) {
cerr<<"顶点数应该大于0且小于"<<maxvertex<<endl;
return;
}
graph.vertexnum=n;
graph.connect=new double*[graph.vertexnum];
for(i=0;i<graph.vertexnum;i++) {
graph.connect[i]=new double[graph.vertexnum];
}
for(i=0;i<graph.vertexnum;i++) {
for(j=0;j<graph.vertexnum;j++) graph.connect[i][j]=0;
}
this->visited=new bool[graph.vertexnum];
graph.array=new tnode[graph.vertexnum];
if(!graph.array) {
cerr<<"内存分配失败!"<<endl;
return;
}
cout<<"请输入顶点信息"<<endl;
ElemType vertex;
for(i=0;i<graph.vertexnum;i++) {
cout<<"请输入第"<<i<<"个顶点:";
cin>>vertex;
if(!cin.good()) {
cerr<<"输入顶点格式不合法!"<<endl;
i--;
continue;
}
else {
graph.array[i].vertex=vertex;
graph.array[i].link=0;
}
}
cout<<"请输入边信息"<<endl;
int m=0;
int from; //边的起点
int to; //边的终点
double weight;
cout<<"如果输入边的起点或终点是-1时,则程序停止接受输入!"<<endl;
int k=0; //边的个数
lnode *p;
while(true) {
cout<<"边的起点:";
cin>>from;
if(!cin.good()) {
cerr<<"输入起点不合法!"<<endl;
break;;
}
if(from==-1) break;
else if(from<0 || from>=graph.vertexnum) {
cerr<<"输入范围应该大于等于0且小于"<<graph.vertexnum<<endl;
break;;
}
cout<<"边的终点:";
cin>>to;
if(!cin.good()) {
cerr<<"输入终点不合法!"<<endl;
break;
}
if(to==-1) break;
else if(to<0 || to>=graph.vertexnum) {
cerr<<"输入范围应该大于等于0且小于"<<graph.vertexnum<<endl;
return;
}
if(IsWeight==true) {
cout<<"输入边的权值:";
cin>>weight;
if(!cin.good()) {
cout<<"输入权值不合法!"<<endl;
break;
}
if(weight>=maxweight || weight<0) {
cerr<<"权值应该大于等于0且小于"<<maxweight<<endl;
break;
}
}
p=new lnode;
p->next=0;
p->sequence=to;
if(IsWeight) graph.connect[from][to]=weight;
else graph.connect[from][to]=1;
p->next=graph.array[from].link; //插入邻接链表
graph.array[from].link=p;
if(!IsDirect) { //是无向图
lnode *q=new lnode;
q->next=0;
q->sequence=from;
if(IsWeight) graph.connect[to][from]=weight;
else graph.connect[to][from]=1;
q->next=graph.array[to].link;
graph.array[to].link=q;
}
k++;
}
graph.edgenum=k;
}
template<class ElemType>
void Graph<ElemType>::DFS() {
int i;
for(i=0;i<graph.vertexnum;i++) {
this->visited[i]=false;
}
for(i=0;i<graph.vertexnum;i++) {
if(visited[i]==false) DFSTranverse(i);
}
}
template<class ElemType>
void Graph<ElemType>::DFSTranverse(int start) {
visited[start]=true;
cout<<"第"<<start<<"个顶点是:"<<graph.array[start].vertex<<endl;
int j;
lnode *p=graph.array[start].link;
while(p) {
j=p->sequence;
if(!this->visited[j]) DFSTranverse(j);
p=p->next;
}
}
template<class ElemType>
void Graph<ElemType>::Get(int index,ElemType &e) {
if(index<0 || index>=graph.vertexnum) {
cerr<<"索引号应该大于等于0且小于"<<graph.vertexnum<<endl;
exit(1);
}
e=graph.array[index].vertex;
}
template<class ElemType>
void Graph<ElemType>::show() {
int m,n;
double weight;
n=graph.vertexnum;
m=graph.edgenum;
cout<<"图有"<<n<<"个顶点,"<<m<<"条边"<<endl;
cout<<"图的顶点信息:"<<endl;
this->DFS();
int i,j;
int from,to;
int k=1;
ElemType e;
cout<<"图的边信息:"<<endl;
for(i=0;i<n;i++) {
for(j=0;j<n;j++) {
if(graph.connect[i][j]) {
from=i;
to=j;
cout<<endl;
cout<<"第"<<k++<<"条边"<<endl;
cout<<"起点:";
e=graph.array[from].vertex;
cout<<e<<" ";
cout<<"终点:";
e=graph.array[to].vertex;
cout<<e<<" ";
if(IsWeight)
{
weight=graph.connect[i][j];
cout<<"权值:"<<weight<<endl;
}
}
}
}
}
template<class ElemType>
Graph<ElemType>::~Graph() {
delete []visited;
visited=0;
int i;
lnode *p,*q;
for(i=0;i<graph.vertexnum;i++) {
p=graph.array[i].link;
while(p) {
q=p;
p=p->next;
graph.array[i].link=p;
delete q;
}
}
delete []graph.array;
graph.array=0;
for(i=0;i<graph.vertexnum;i++) {
delete []graph.connect[i];
graph.connect[i]=0;
}
delete graph.connect;
graph.connect=0;
}
3. 测试图类
Graph<char>g1(0,0); //不带权值的无向图
g1.show();
4.
Graph<char>g2(0,1); //带权值的无向图
g2.show();
Graph<char>g3(1,0); //不带权值的有向图
g3.show();
Graph<char>g4(0,0); //带权值的有向图
g4.show();
4. 二部图的判定及匈牙利算法
#include "graph.h"
#include "graph.cpp"
#include <string>
#include <vector>
#include <map>
using namespace std;
enum Color{red,blue,black}; //定义颜色
template<class ElemType>
class Bipartite_Graph:public Graph<ElemType> {
private:
bool flag; //表示此图是不是二分图
vector<int>Red;
vector<int>Blue;
Color *vertexcolor;
map<int,int>match; //<蓝点,与蓝点匹配的红点>
int count; //匹配数
public:
Bipartite_Graph(bool IsDirect,bool IsWeight):Graph<ElemType>(IsDirect,IsWeight) {
vertexcolor=new Color[graph.vertexnum];
flag=true;
count=0;
int i,j;
for(i=0;i<graph.vertexnum;i++) vertexcolor[i]=black;
for(i=0;i<graph.vertexnum;i++) visited[i]=false;
for(i=0;i<graph.vertexnum;i++) {
if(visited[i]==false) {
IsBipartite_Graph_DFS(i,red); //判断输入的图是不是二部图,如果是,则将图的顶点划分为红点集和蓝点集
if(flag==false) return;
}
}
count=0;
vector<int>::iterator it;
for(it=Blue.begin();it!=Blue.end();it++) {
pair<int,int>p(*it,-1); // <蓝点,-1>
match.insert(p);
}
}
void Hungary() { //匈牙利算法
vector<int>::iterator it;
for(it=Red.begin();it!=Red.end();it++) {
for(int i=0;i<graph.vertexnum;i++) visited[i]=false;
if(DFS(*it)) count++;
}
ElemType red,blue;
cout<<"最大匹配是:"<<endl;
map<int,int>::iterator pos;
for(pos=match.begin();pos!=match.end();pos++) {
if(pos->first==-1 || pos->second==-1) continue; //此时match中有顶点没有匹配
Get(pos->first,blue);
Get(pos->second,red);
cout<<"红点:"<<red<<"到"<<"蓝点"<<blue<<endl;
}
cout<<"共有"<<count<<"个匹配"<<endl;
}
void show() {
Graph<ElemType>::show();
vector<int>::iterator it;
cout<<endl<<"红点集是:"<<endl;
for(it=Red.begin();it!=Red.end();it++) {
cout<<*it<<" ";
}
cout<<endl;
cout<<endl<<"蓝点集是:"<<endl;
for(it=Blue.begin();it!=Blue.end();it++) {
cout<<*it<<" ";
}
cout<<endl;
}
~Bipartite_Graph() {
delete []vertexcolor;
int i;
}
private:
void IsBipartite_Graph_DFS(int start,Color color) { //pre是顶点start在DFS序列中的直接前驱顶点
int j;
visited[start]=true;
cout<<start<<" ";
if(color==red) cout<<"red"<<endl;
else cout<<"blue"<<endl;
if(vertexcolor[start]==black) { //此顶点没有打上颜色
vertexcolor[start]=color;
if(color==red) {
Red.push_back(start); //将此顶点加入红点集
color=blue; //将颜色转化
}
else {
Blue.push_back(start);
color=red;
}
}
lnode *p=graph.array[start].link;
while(p) {
j=p->sequence;
if(!visited[j]) {
if(flag==true) IsBipartite_Graph_DFS(j,color); //去检查此顶点的相邻顶点
}
else {
if(vertexcolor[j]==vertexcolor[start]) { //相关联的两个顶点颜色一样,则此图不是二分图
cerr<<"不是二分图!"<<endl;
Red.clear();
Blue.clear();
flag=false;
return;
}
}
p=p->next;
}
}
bool DFS(int k) {
vector<int>::iterator it;
for(it=Blue.begin();it!=Blue.end();it++) //试探每一个蓝点集的顶点
{
if(graph.connect[k][*it] && !visited[*it]) //如果此蓝点与红点相连,且该蓝点没有被访问过
{
visited[*it]=true;
if(match[*it]==-1 || DFS(match[*it])) //如果该蓝点没有与其匹配的红点,或者蓝点
//有匹配的红点,那么从该红点查找匹配
{
match[*it]=k; //找到增广路径,将其取反
return true;
}
}
}
return false;
}
};
void main() {
Bipartite_Graph<string>bg(0,0);
map<int,int>::iterator it;
bg.show();
bg.Hungary();
}