实验五 图的操作

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

实验代码

#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;
}

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值