实验代码
#include <iostream>
using namespace std;
const int MAX_VERTEX_NUM = 20; //矩阵一个维度上的顶点
const int MAX = 65535; //表示矩阵中没有边相连
//int visited[MAX_VERTEX_NUM] = {0}; //标记是否被访问
const int QUEUE_INIT_SIZE = 20;
const int STACK_INIT_SIZE = 20;
const int ERROR = 0;
const int OK = 1;
const int EMPTY = 1;
typedef int Status;
template <class T>
class SqStack
{
public:
SqStack();
~SqStack();
Status Push(T e);
Status Pop(T &e);
Status GetTop(T &e) const;
int StackLength() const;
Status IsEmpty();
void DispStack();
private:
T *base; //栈的起始地址
T *top; //栈顶指针
int stacksize;
};
//初始化一个栈
template <class T>
SqStack<T>::SqStack()
{
base = new T[STACK_INIT_SIZE];
top = base;
stacksize = STACK_INIT_SIZE;
}
//释放一个栈
template <class T>
SqStack<T>::~SqStack()
{
delete[] base;
}
//取顺序栈栈顶元素算法
template <class T>
Status SqStack<T>::GetTop(T &e) const
{
if (top == base)
{
return ERROR;
}
e = *(top - 1);
return OK;
}
//顺序栈入栈
template <class T>
Status SqStack<T>::Push(T e)
{
if (top - base >= stacksize)
{
return ERROR;
}
*top++ = e; //先赋值,再加指针
return OK;
}
//顺序栈出栈
template <class T>
Status SqStack<T>::Pop(T &e)
{
if (top == base)
{
return ERROR;
}
e = *--top; //先减指针,再取值
return OK;
}
template <class T>
int SqStack<T>::StackLength() const
{
int sizeCount = 0;
T *p = top - 1;
while (p >= base)
{
sizeCount++;
p--;
}
return sizeCount;
}
template <class T>
Status SqStack<T>::IsEmpty() //空栈为1,非空栈为0;
{
if (top == base)
{
return EMPTY;
}
else
{
return !EMPTY;
}
}
template <class T>
void SqStack<T>::DispStack()
{
T *p = top - 1;
while (p >= base)
{
cout << *p << " ";
p--;
}
cout << endl;
}
template <class T>
class CircularQueue
{
public:
CircularQueue();
~CircularQueue();
Status EnQueue(T e);
Status DeQueue(T &e);
int QueueLength() const;
Status IsEmpty();
void DispQueue();
private:
T * base; //内存空间首地址
int front; //头指针
int rear; //尾指针
int queueSize; //队列的长度
};
template <class T>
CircularQueue<T>::CircularQueue()
{
base = new T[QUEUE_INIT_SIZE];
if (!base)
{
cerr << "存储分配错误!~" << endl;
}
front = rear = 0;
queueSize = QUEUE_INIT_SIZE;
}
template <class T>
CircularQueue<T>::~CircularQueue()
{
delete[] base;
}
template <class T>
int CircularQueue<T>::QueueLength() const
{
//确保其为一个正数再取模
return (rear - front + QUEUE_INIT_SIZE) % QUEUE_INIT_SIZE;
}
template <class T>
void CircularQueue<T>::DispQueue()
{
int p = front;
while (p != rear)
{
cout << base[p] << " ";
p = (p + 1) % QUEUE_INIT_SIZE;
}
cout << endl;
}
template <class T>
Status CircularQueue<T>::EnQueue(T e)
{
if ((rear + 1) % QUEUE_INIT_SIZE == front)
{
return ERROR;
}
base[rear] = e;
rear = (rear + 1) % QUEUE_INIT_SIZE;
return OK;
}
template <class T>
Status CircularQueue<T>::DeQueue(T &e)
{
if (rear == front)
{
return EMPTY;
}
e = base[front];
front = (front + 1) % QUEUE_INIT_SIZE;
return OK;
}
template <class T>
Status CircularQueue<T>::IsEmpty()
{
return (rear == front) ? EMPTY : !EMPTY;
}
//邻接矩阵类型描述
//矩阵中的每一个元素结构
typedef struct ArcCell
{
int adj; //无权图,用1或0表示相邻否
//对带权图,则为权值
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
//节点信息结构体
typedef struct ArcInfo
{
char From; //起点
char To; //终点
int weight; //权重
}ArcInfo;
//邻接矩阵类定义
class MGraph
{
public:
void createMGraph();
void printNode();
void PrintMatrix();
void DFSTraverse(char v);
void DFSTraverseFull(char v);
void DFS(char cVex);
void BFSTraverse(char v);
void BFSTraverseFull(char v);
int prim(char v);
int Kruskal();
int LocateVex(char cVex);
void InsertSort(ArcInfo* E, int n);
private:
char vexs[MAX_VERTEX_NUM]; //顶点信息(A,B...)
AdjMatrix arcs; //弧的信息,为(0,1),对于无向图,对于有向图则为权,为一个矩阵
int vexnum; //顶点数,矩阵一个维度上的元素个数
int arcnum; //弧数,矩阵的边的条数
int visited[MAX_VERTEX_NUM] = { 0 }; //标记是否被访问
};
//邻接表类型描述
//邻接表节点
typedef struct ArcNode
{
int adjvex; //邻接点位置,数组下标从零开始
int weight; //权值
struct ArcNode *nextarc;
}ArcNode;
//表头节点
typedef struct VNode
{
char data; //节点数据
int in; //输入权值
ArcNode *firstarc; //指向下一个的指针
}VNode, AdjList[MAX_VERTEX_NUM];
//邻接表描述
class ALGraph
{
public:
//ALGraph();
//~ALGraph();
void createGraph();
void createGraph(int vnum, int anum, char data[], ArcInfo arcList[]);
void DispGraph();
int TopOrder();
int TopOrder(int ve[], SqStack<char> &out);
void cal_indegree(); //统计每个顶点的入度
void critical_path();
int LocateVex(char cVex); //根据顶点信息,返回顶点的坐标
void printIn();
private:
int vexnum; //顶点数
int arcnum; //弧数,也即图中的边数
AdjList vertices; //表头节点数组,也即图中所有的节点
};
//创建无向网图的邻接矩阵表示
void MGraph::createMGraph()
{
cout << "请输入顶点数和边数:";
cin >> vexnum >> arcnum; //初始化顶点和边
for (int i = 0; i<vexnum; i++)
{
cout << "读入顶点信息:";
cin >> vexs[i]; //读入顶点信息
}
for (int i = 0; i<vexnum; i++)
{
for (int j = 0; j<vexnum; j++)
{
arcs[i][j].adj = MAX; //邻接矩阵初始化
}
}
//读入边的信息
for (int k = 0; k<arcnum; k++)
{
int i, j, w;
cout << "输入边(vi,vj)上的下标i,下标j和权重w:";
cin >> i >> j >> w;
arcs[i][j].adj = w; //更新边的权值
arcs[j][i].adj = w; //无向图,矩阵对称
}
}
//打印图中的顶点
void MGraph::printNode()
{
for (int i=0; i<vexnum; i++)
{
cout << vexs[i] << " ";
}
cout << endl;
}
//打印邻接矩阵
void MGraph::PrintMatrix()
{
for (int i = 0; i<vexnum; i++)
{
for (int j = 0; j<vexnum; j++)
{
cout << arcs[i][j].adj << "\t";
}
cout << endl;
}
}
//DFS搜索
//邻接矩阵实现
void MGraph::DFSTraverse(char v)
{
for (int i = 0; i<vexnum; i++)
{
visited[i] = 0; //将visited数组设置为0,表示都未被访问过
}
cout << "深度遍历序列为:";
DFS(v); //递归遍历
cout << endl;
}
//对每个节点尝试深度优先遍历
void MGraph::DFSTraverseFull(char v)
{
for (int i = 0; i<vexnum; i++)
{
visited[i] = 0;
}
cout << "深度遍历序列为:";
DFS(v);
for (int i = 0; i<vexnum; i++)
{
if (!visited[i])
{
DFS(vexs[i]);
}
}
cout << endl;
}
//cVex为开始进行遍历的字符
void MGraph::DFS(char cVex)
{
cout << cVex << " "; //访问当前字符
int pos = LocateVex(cVex); //获取字符cVex在数组中的下标
visited[pos] = 1; //标识当前字符被访问过
for (int i = 0; i<vexnum; i++)
{
if ((arcs[pos][i].adj != MAX) && visited[i] == 0) //没有边相连对应于矩阵中的无穷大MAX,
{
DFS(vexs[i]);
}
}
}
//广度优先搜索
void MGraph::BFSTraverse(char v)
{
//由于要求相对顺序不变
//此处用到循环队列
CircularQueue<char> q;
char a;
//初始化
for (int i = 0; i<vexnum; i++)
{
visited[i] = 0;
}
cout << "广度遍历序列为:";
//访问第一个节点
cout << v << " ";
int pos = LocateVex(v);
visited[pos] = 1; //标记为已经访问过
q.EnQueue(v);
while (!q.IsEmpty())
{
q.DeQueue(a); //字符a出队
pos = LocateVex(a);
for (int i = 0; i<vexnum; i++)
{
//寻找与此节点相连并且未被访问的节点
if ((arcs[pos][i].adj != MAX) && visited[i] == 0)
{
visited[i] = 1; //设置访问标记
cout << vexs[i] << " "; //操作相关节点
q.EnQueue(vexs[i]); //进队保持有序性
}
}
}
cout << endl;
}
//非连通图的广度优先搜索
void MGraph::BFSTraverseFull(char v)
{
CircularQueue<char> q;
char a;
//初始化
for (int i = 0; i<vexnum; i++)
{
visited[i] = 0;
}
cout << "广度遍历序列为:";
//访问第一个节点
cout << v << " ";
int pos = LocateVex(v); //头结点定位
visited[pos] = 1; //标记已经访问过
q.EnQueue(v);
while (!q.IsEmpty())
{
q.DeQueue(a); //队中元素按照相对顺序出队
pos = LocateVex(a);
for (int i = 0; i<vexnum; i++)
{
//寻找与当前节点相连并且未被访问的节点
if ((arcs[pos][i].adj != MAX) && visited[i] == 0)
{
visited[i] = 1; //设置访问标记
cout << vexs[i] << " "; //访问的具体操作
q.EnQueue(vexs[i]); //将此元素进队,便于后续操作
}
}
}
for (int i = 0; i<vexnum; i++)
{
if (visited[i] == 0)
{
cout << vexs[i] << " ";
visited[i] = 1;
q.EnQueue(vexs[i]);
while (!q.IsEmpty())
{
q.DeQueue(a);
pos = LocateVex(a);
for (int i = 0; i<vexnum; i++)
{
if ((arcs[pos][i].adj != MAX) && visited[i] == 0)
{
visited[i] = 1;
cout << vexs[i] << " ";
q.EnQueue(vexs[i]);
}
}
}
}
}
cout << endl;
}
//Prim算法求最小生成树
int MGraph::prim(char v)
{
int i, j, k, min, sum = 0;
int pos = LocateVex(v);
//顶点权值
int lowcost[MAX_VERTEX_NUM];
//最小生成树节点
//存放顶点序号
int closet[MAX_VERTEX_NUM];
for (i = 0; i<vexnum; i++)
{
//lowcost[i]记录以i为终点的边的最小权值
//当lowcost[i]=0时表示终点i加入u
//从这个节点开始到其余节点的权值
lowcost[i] = arcs[pos][i].adj;
//closet[i]记录对应lowcost[i]的起点
closet[i] = pos;
}
//将初始点要放入v集合
lowcost[pos] = 0; //到自身的权值为0
cout << "Prim最小生成树的边为:" << endl;
//n个节点至少需要n-1条边构成最小生成树
for (i = 1; i<vexnum; i++)
{
min = MAX;
for (j = 0; j < vexnum; j++)
{
//寻找最短的边
if (lowcost[j] != 0 && lowcost[j] < min)
{
min = lowcost[j];
k = j; //k指示的是最小值对应的下标
}
}
//找到最短边之后累加权值
sum += lowcost[k];
//输出路径:输出刚刚找到的最短路径的树枝
cout << "(" << vexs[closet[k]] << ",";
cout << vexs[k] << ")=" << lowcost[k] << endl;
//将找到的k并入u集合,表示这个点不能用了
lowcost[k] = 0;
//更新当前节点minid到其他节点的权值
for (j = 0; j<vexnum; j++)
{
//发现更小的权值
if (arcs[k][j].adj != MAX && arcs[k][j].adj<lowcost[j])
{
//更新权值信息
lowcost[j] = arcs[k][j].adj;
//更新最小权值边的起点
closet[j] = k;
}
}
}
return sum;
}
//Kruskal算法求最小生成树
int MGraph::Kruskal()
{
int i, j, k;
int sum = 0; //统计所有路径的长度之和
int vset[MAX_VERTEX_NUM]; //用于存放每个顶点所属的集合编号,
//属于同一个集合则有可能成环
ArcInfo E[30]; //存放所有的路径
k = 0; //统计一共有多少条路径 == arcnum
//初始化操作
for(i=0; i<vexnum; i++)
{
for(j=0; j<vexnum; j++)
{
if(arcs[i][j].adj != MAX)
{
E[k].From = vexs[i];
E[k].To = vexs[j];
E[k].weight = arcs[i][j].adj;
k++;
}
}
}
InsertSort(E, 2*arcnum); //无向图存储的是两倍的边
//初始化辅助数组
for(i=0; i<vexnum; i++)
{
vset[i] = i;
}
k = 1; //循环执行次数
j = 0; //标记当前处理的边
int u1, v1; //指定顶点在vexs中的下标
int sn1, sn2; //对应顶点在vset中的下标
cout << "Kruskal最小生成树的边为:" << endl;
while(k < vexnum) //vexnum个顶点只需要找vexnum-1条边
{
u1 = LocateVex(E[j].From);
v1 = LocateVex(E[j].To);
sn1 = vset[u1];
sn2 = vset[v1];
if(sn1 != sn2)
{
cout << "(" << E[j].From << "," << E[j].To << ")=" << E[j].weight << endl;
sum += E[j].weight; //更新总权重
k++;
//将有边相连的两个顶点并入同一个集合
for(i=0; i<vexnum; i++)
{
if(vset[i] == sn2)
{
vset[i] = sn1;
}
}
}
j++;
}
return sum;
}
//获取字符cVex在数组中的下标
int MGraph::LocateVex(char cVex)
{
for (int i = 0; i<vexnum; i++)
{
if (vexs[i] == cVex) //直接在顶点数组中寻找
{
return i; //找到返回下标
}
}
return -1; //未找到
}
//直接选择排序
void MGraph::InsertSort(ArcInfo* E, int n)
{
int min = -1;
for (int i = 0; i < n-1; i++)
{
min = i;
for (int j = i + 1; j < n; j++)
{
if (E[j].weight < E[min].weight)
{
min = j;
}
}
if (min != i)
{
swap(E[i], E[min]);
}
}
}
void swap(ArcInfo &a, ArcInfo &b)
{
ArcInfo temp;
temp.From = a.From;
temp.To = a.To;
temp.weight = a.weight;
a.From = b.From;
a.To = b.To;
a.weight = b.weight;
b.From = temp.From;
b.To = temp.To;
b.weight = temp.weight;
}
/******************************************************/
void ALGraph::createGraph()
{
int i, j, k;
int weight = 0;
ArcNode *e;
cout << "请输入顶点数和边数:";
cin >> vexnum >> arcnum;
for(i=0; i<vexnum; i++)
{
//输入顶点信息
cout << "请输入顶点:";
cin >> vertices[i].data;
//cout << "请输入顶点的入度";
//cin >> vertices[i].in = 0;
vertices[i].in = 0; //每个顶点的入度置为0
vertices[i].firstarc = NULL; //将边表置为空表
}
//建立边表
for(k=0; k<arcnum; k++)
{
cout << "输入边(vi, vj)上的顶点序号:";
cin >> i >> j;
vertices[j].in++; //序号为j的节点入度加一
cout << "输入该边上的权值:";
cin >> weight;
e = new ArcNode;
e->adjvex = j; //邻接序号为j
e->weight = weight;
e->nextarc = vertices[i].firstarc; //头插法
vertices[i].firstarc = e;
}
printIn();
}
//循环打印入度
void ALGraph::printIn()
{
for(int i=0; i<vexnum; i++)
{
cout << vertices[i].in << " ";
}
cout << endl;
}
//拓扑排序
int ALGraph::TopOrder()
{
SqStack<char> s;
int degree[MAX_VERTEX_NUM]; //将每一个顶点的入度信息copy到degree数组中
int j, count = 0, k;
char node;
ArcNode *p;
for (j = 0; j < vexnum; j++) //初始化degree数组
{
degree[j] = vertices[j].in;
}
for (j = 0; j < vexnum; j++) //入度为0的节点入栈
{
if (degree[j] == 0)
s.Push(vertices[j].data);
}
cout << "拓扑排序的序列为:";
while (!s.IsEmpty()) //出栈度为0的节点,并输出
{
count++;
s.Pop(node);
cout << node << " ";
p = vertices[LocateVex(node)].firstarc; //指向出栈元素对应邻接表第一个元素
while (p != NULL)
{
k = p->adjvex; //邻接点的位置
degree[k]--; //指向它的元素出栈,此节点入度减1
if (degree[k] == 0)
{
s.Push(vertices[k].data); //入度减为0 入栈
}
p = p->nextarc;
}
}
if (count < vexnum)
{
cout << "有环路" << endl;
return 0;
}
else
{
cout << endl;
return 1;
}
}
//拓扑排序求Ve[]
int ALGraph::TopOrder(int ve[], SqStack<char> &out)
{
//定义一个堆栈
SqStack<char> s;
//将入度信息copy到degree数组中
int degree[MAX_VERTEX_NUM];
int j, count = 0, k, pos;
char node;
ArcNode *p;
for (j = 0; j < vexnum; j++)
{
degree[j] = vertices[j].in;
//初始化各个顶点的最早开始时间为0
ve[j] = 0;
}
//将入度为0的节点入栈
for (j = 0; j < vexnum; j++)
{
if (degree[j] == 0)
{
s.Push(vertices[j].data);
}
}
//出栈度为0的节点,并输出
while (!s.IsEmpty())
{
count++;
s.Pop(node);
out.Push(node);
p = vertices[LocateVex(node)].firstarc;
pos = LocateVex(node);
while (p != NULL)
{
k = p->adjvex;
degree[k]--; //入度减1
if (degree[k] == 0) //入度减为0 入栈
{
s.Push(vertices[k].data);
}
if (ve[pos] + p->weight > ve[k]) //更新求最长的路径
{
ve[k] = ve[pos] + p->weight;
}
p = p->nextarc;
}
}
if (count < vexnum)
{
cout << "有环路" << endl;
return 0;
}
else
{
return 1;
}
}
//逆拓扑排序求v1[]
void ALGraph::critical_path()
{
int i, k, ee, el, pos;
char node, tag;
ArcNode *p;
//最早开始时间,最迟开始时间
int ve[MAX_VERTEX_NUM], vl[MAX_VERTEX_NUM];
SqStack<char> out;
i = TopOrder(ve, out);
if (!i)
{
cout << "有回路!";
}
else
{
for (i = 0; i < vexnum; i++)
{
vl[i] = MAX; //初始化最迟开始的时间为最大值
}
vl[vexnum - 1] = ve[vexnum - 1];
while (!out.IsEmpty())
{
out.Pop(node);
pos = LocateVex(node);
p = vertices[LocateVex(node)].firstarc;
while (p != NULL)
{
k = p->adjvex;
if (vl[k] - p->weight < vl[pos])
{
vl[pos] = vl[k] - p->weight;
}
p = p->nextarc;
}
}
//打印每条路径,并标出关键路径
for (int j = 0; j < vexnum; j++)
{
p = vertices[j].firstarc;
while (p != NULL)
{
k = p->adjvex;
ee = ve[j];
el = vl[k] - p->weight;
if (ee == el)
{
tag = '*';
}
else
{
tag = ' ';
}
cout << "(" << vertices[j].data << "," << vertices[k].data
<< "),w=" << p->weight << ",ee=" << ee << ",el=" << el << " "
<< tag << endl;
p = p->nextarc;
}
}
}
}
//定位顶点
int ALGraph::LocateVex(char cVex)
{
for (int i = 0; i<vexnum; i++)
{
if (vertices[i].data == cVex) //直接在顶点数组中寻找
{
return i; //找到返回下标
}
}
return -1; //未找到
}
int main()
{
MGraph graph;
graph.createMGraph();
cout << "-----打印顶点-----" << endl;
graph.printNode();
cout << "-----打印权值-----" << endl;
graph.PrintMatrix();
cout << "-----深度优先搜索-----" << endl;
graph.DFSTraverse('A');
cout << "*****************" << endl;
graph.DFSTraverseFull('A');
cout << "-----广度优先搜索-----" << endl;
graph.BFSTraverse('A');
cout << "*****************" << endl;
graph.BFSTraverseFull('A');
cout << "-----Prim算法求最小生成树-----" << endl;
int sum = graph.prim('A');
cout << "权值之和为:" << sum << endl;
cout << "-----Kruskal算法求最小生成树-----" << endl;
sum = graph.Kruskal();
cout << "权值之和为:" << sum << endl;
cout << "*********************************" << endl;
ALGraph agraph;
agraph.createGraph();
cout << "-----拓扑排序-----" << endl;
agraph.TopOrder();
cout << "-----关键路径-----" << endl;
agraph.critical_path();
return 0;
}