图 学习总结

图的基本内容

邻接表和邻接矩阵的实现方式将在具体算法实现中完成,不做赘述。
邻接矩阵的遍历:
1.深度优先遍历:(递归实现)一直走不回头
(1)访问指定起始地点。
(2)若当前访问顶点的邻接顶点有未被访问的顶点,就任选一个访问。如果没有就回退到最近访问的顶点,直到与起始顶点相通的所有点被遍历完。
(3)若途中还有顶点未被访问,则再选一个点作为起始顶点。重复步骤2(针对非连通图)。
2.广度优先遍历:(队列实现)走完一个点的所有临界点再往下走
(1)访问指定起始点。
(2)访问当前顶点的邻接顶点有未被访问的顶点,并将之放入队列中。
(3)删除队列的队首节点。访问当前队列的队首,重复步骤2。直到队列为空。
(4)若若途中还有顶点未被访问,则再选一个点作为起始顶点。重复步骤2。(针对非连通图)。

#include<iostream>
#include<queue>
using namespace std;
const int MaxSize = 100;
int MinEdge(int a[],int n)
{
    int min = 100;
    int index = 1;
    for(int i = 1;i<=n;i++)
    {
        if(a[i]<=min&&a[i]!=0)
        {
            min = a[i];
            index = i;
        }
    }
    return index;
}
template <typename DataType>
class MGraph
{
    public:
    MGraph(DataType a[],int n,int e);
    ~MGraph(){};
    void DFT(int v);
    void BFT(int v);
    private:
    DataType vertex[MaxSize];
    int visted[MaxSize];
    int edge[MaxSize][MaxSize];
    int vertexNum,edgeNum;
};
template <typename DataType>
MGraph<DataType>::MGraph(DataType a[],int n,int e)
{
    vertexNum = n;
    edgeNum = e;
    int w;
    for(int i = 1;i<=n;i++)
    {
        vertex[i] = a[i];
    }
    for(int i = 1;i<=n;i++)
    {
        visted[i] = 0;
    }
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=n;j++)
        {
            cin>>edge[i][j];
        }
    }
}
template <typename DataType>
void MGraph<DataType>::DFT(int v)//深度优先搜索
{
    cout<<vertex[v];//输出顶点具体信息
    visted[v] = 1;//修改标记
    for(int i = 1;i<=vertexNum;i++)//遍历图中的所有顶点
    {
        if(visted[i]!=1&&edge[v][i]<100)//如果与已知顶点相邻并且没有被访问过
        DFT(i);//深度优先遍历这个邻接点
    }
}
template <typename DataType>
void MGraph<DataType>::BFT(int v)//广度优先搜索
{
    queue<int>q;
    q.push(v);//起始顶点无条件入队
    cout<<vertex[v];//输出顶点信息
    visted[v] = 1;//修改起始点的访问标识符
    while(!q.empty())//当队列不空时
    {
        v = q.front();//取队首元素
        q.pop();//删除队首元素
        for(int i = 1;i<=vertexNum;i++)//遍历所有的顶点
        {
            if(visted[i]!=1&&edge[v][i]<100)//如果与已知顶点相邻并且没有被访问过
            {
                cout<<vertex[i];
                visted[i] = 1;//修改访问标志
                q.push(i);//邻接点入队
            }
        }
    }
}

邻接表的遍历:
最小生成树:(加点)
1.Prim算法
将图分成两部分,一部分为待求最小生成树,一部分为没有选过的点。
(1)起始最小生成树为空。
(2)将一个点加入到最小生成树的集合中,遍历所有与之相邻的点,选择权值最小的邻接点加入最小生成树集合中。
(3)遍历与新加入顶点相邻的点,选择权值最小的邻接点加入最小生成树中,重复步骤2,直到所有点都加入最小生成树中。

Prim算法的核心是记录了目前未选取顶点与最小生成树的最小权值,选完后更新记录,直到算法完成。

#include<iostream>
#include<queue>
using namespace std;
const int MaxSize = 100;
int MinEdge(int a[],int n)//用于选取最小权值的边
{
    int min = 100;
    int index = 1;
    for(int i = 1;i<=n;i++)
    {
        if(a[i]<=min&&a[i]!=0)
        {
            min = a[i];
            index = i;
        }
    }
    return index;
}
template <typename DataType>
class MGraph
{
    public:
    MGraph(DataType a[],int n,int e);
    ~MGraph(){};
    void Prim(int v);
    private:
    DataType vertex[MaxSize];
    int visted[MaxSize];
    int edge[MaxSize][MaxSize];
    int vertexNum,edgeNum;
};
template <typename DataType>
MGraph<DataType>::MGraph(DataType a[],int n,int e)
{
    vertexNum = n;
    edgeNum = e;
    int w;
    for(int i = 1;i<=n;i++)
    {
        vertex[i] = a[i];
    }
    for(int i = 1;i<=n;i++)
    {
        visted[i] = 0;
    }
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=n;j++)
        {
            cin>>edge[i][j];
        }
    }
}
template <typename DataType>
void MGraph<DataType>::Prim(int v)
{
    int adjvex[MaxSize];//记录与最小生成树相邻的顶点
    //adjvex[i] = j;表达的意思是整个图中顶点i与生成树中的顶点j相邻
    int lowcost[MaxSize];//记录与最小生成树相邻的顶点的最小权值
    //lowcost[i] = w;表达的意思是整个图中顶点i与生成树的最小权值是w
    for(int i = 1;i<=vertexNum;i++)
    {
        lowcost[i] = edge[v][i];
        adjvex[i] = v;//初始图中所有顶点与指定顶点相邻
    }
    lowcost[v] = 0;//将指定顶点加入到最小生成树中
    int j;
    for(int k = 1;k<vertexNum;k++)
    {
        j = MinEdge(lowcost,vertexNum);//选出最小值的点
        cout<<lowcost[j]<<" ";
        lowcost[j] = 0;
        for(int i = 1;i<=vertexNum;i++)//遍历所有的顶点更新记录
        {
            if(edge[i][j]<lowcost[i])
            {
                lowcost[i] = edge[i][j];
                adjvex[i] = j;
            }
        }
    }
}

2.Kruskal算法(加边)
(1)对图中的所有边进行排序;
(2)初始化最小生成树为包括所有点,0条边的树
(3)如果最小权值边与生成树不在同一连通分量,加入最小生成树中
(4)如果在同一连通分量中,略过这条边
(5)重复步骤4和5,直到选出顶点数-1条边

使用边集数组存储边,应用并查集合并。

合并两棵树:构建父子关系

判断两棵树是否为同一连通分量的方案:检查它们的根是否相同

#include <iostream>
#include <algorithm>
using namespace std;
struct Edge{//定义边集
	int from,to,weight;
};
bool cmp(Edge&e1,Edge&e2){
	return e1.weight<e2.weight;
}
class EdgeSet
{
	private:
	Edge*edge;
	int vertexNum;
	int edgeNum;
	int FindRoot(int*parent,int index);//根查找函数
	public:
	EdgeSet(int n,int e);
	~EdgeSet(){
		
	};
	void Kruskal();
};
EdgeSet::EdgeSet(int n,int e){
	vertexNum = n;
	edgeNum = e;
	edge = new Edge[edgeNum];
	int index = 0;
	int a[vertexNum+1][vertexNum+1];
	for(int i = 1;i<=vertexNum;i++)
		for(int j = 1;j<=vertexNum;j++){
		cin>>a[i][j];
	}
	for(int i = 1;i<=vertexNum;i++)
		for(int j = i;j<=vertexNum;j++){
		if(a[i][j]<100&&a[i][j]>0){
			edge[index].from = i;
			edge[index].to = j;
			edge[index].weight = a[i][j];
			index++;
		}
	}
	sort(edge,edge+index,cmp);
}
int EdgeSet::FindRoot(int*parent,int index){
	int root = index;
	while(parent[root]>-1){
		root = parent[root];
	}
	return root;
}
void EdgeSet::Kruskal(){
	int*parent = new int[vertexNum+1];
	for(int i = 0;i<=vertexNum;i++)
		parent[i] = -1;//所有的顶点均没有双亲
	int count = 0;//记录边数
	for(int k = 0;k<edgeNum;k++){
		int i = FindRoot(parent,edge[k].from);
		int j = FindRoot(parent,edge[k].to);
		if(i!=j){//判断是否为同一连通分量
			cout<<edge[k].from<<" "<<edge[k].to<<" ";
			parent[j] = i;//合并
			count++;
			if(count==vertexNum-1)
				break;
		}
	}
}

最短路径算法(单源点到所有点)
1.Dijkstra算法:
在与指定单源点邻接的所有点中,必有一条最短路径长度是两点之间的权值。因此最短路径可以分成两种情况:一种是已经存在的路径,另一种是经过其他的顶点构建的新的路径
(1)初始路径为单源点相邻的顶点的权值,如果没有邻接则用一个默认的最大值代替
(2)选择最小权值的点
(3)更新最短路径长度

void MGraph::Dijkstra(int v){
	int*dist = new int[vertexNum];//记录单源点到各顶点的路径长度,dist[i]:单源点到第i点的路径长度
	string*path = new string[edgeNum];//用于打印路径具体情况
	for(int i = 0;i<vertexNum;i++){//初始化
		dist[i] = edge[v][i];
		if(dist[i]!=MaxSize)
			path[i] = vertex[v]+" "+vertex[i];
		else
			path[i] = "";
	}
	int index;
	for(int num = 1;num<vertexNum;num++){
		index = Min(dist,vertexNum);//选择
		for(int i = 0;i<vertexNum;i++){//尝试所有的点
			if(dist[i]>dist[index]+edge[index][i]){//更新
				dist[i] = dist[index]+edge[index][i];
				path[i] = path[index]+" "+vertex[i];
			}
		}
		dist[index] = 0;//加入最短路径
	}
}

2.Floyd算法:
相对于Dijkstra更朴素的算法,走所有的路,选最短的那条。
n与n个点之间的所有路径,可以调用n次Dijkstra算法等价实现

void MGraph::Floyd(){
	int dist[MaxSize][MaxSize];//记录路径长度
	string path[MaxSize][MaxSize];
	for(int i = 0;i<vertexNum;i++){//初始化路径长度为权值大小
		for(int j = 0;j<vertexNum;j++){
			dist[i][j] = edge[i][j];
			if(edge[i][j]!=MaxSize&&i!=j){
				path[i][j] = vertex[i]+" "+vertex[j];
			}
			else
				path[i][j] = "";
		}
	}
	for(int k = 0;k<vertexNum;k++){//在第i个点和第j个点之间尝试所有的点
		for(int i = 0;i<vertexNum;i++){
			for(int j = 0;j<vertexNum;j++){
				if(dist[i][j]>dist[i][k]+dist[k][j]){//更新
					dist[i][j] = dist[i][k]+dist[k][j];
					path[i][j] = path[i][k]+" "+path[k][j].substr(3);
				}
			}
		}
	}
}

拓扑排序:
先说一下拓扑排序的概念:
对一个有向无环图G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
———出自百度
算法描述:
(1)选择没有箭头指入的节点中的任一个,可以作为合法序列的起点,放入序列。
(2)当我们将某个节点放入序列后,去掉该节点以及从该节点指出的所有箭头。在新的图中,选择没有箭头指向的节点,作为序列中的下一个点。
(3) 重复上面的过程,直到所有的节点都被放入序列。

可以用队列实现以上算法
(1)如果顶点的入度为0,加入队列中
(2)当队列不为空时
a.访问队首元素
b.将所有与队首元素相连的所有点入度减一
c.检查邻接点的入度是否为0,为0入队

#include <iostream>
#include <string.h>
#include <stack>
#include <sstream>
using namespace std;
string itos(int i){
	stringstream s;
	s<<i;
	return s.str();
}
struct EdgeNode{
	int index;
	EdgeNode*next;
};
struct Vertex{
	string vertex;
	int in;
	EdgeNode*first;
};
class AdjList{
	private:
	Vertex*v;
	int vertexNum;
	int edgeNum;
	public:
	AdjList(int n,int e);
	~AdjList();
	void TopSort();
};
AdjList::AdjList(int n,int e){
	vertexNum = n;
	edgeNum = e;
	v = new Vertex[vertexNum+1];
	for(int i = 1;i<=vertexNum;i++){
	    v[i].in = 0;
	    v[i].first = NULL;
	    v[i].vertex = "v"+itos(i);
	}
	int from,to;
	for(int i = 1;i<=edgeNum;i++){
		cin>>from>>to;
		EdgeNode*s = new EdgeNode;
		s->index = to;
		s->next = v[from].first;
		v[from].first = s;
		v[to].in++;
	}
}
AdjList::~AdjList(){
	EdgeNode*p = NULL;
	EdgeNode*q = NULL;
	for(int i = 1;i<=vertexNum;i++){
		p = q = v[i].first;
		while(p!=NULL){
			p = p->next;
			delete q;
			q = p;
		}
	}
}
void AdjList::TopSort(){
	stack<Vertex>s;
	for(int i = vertexNum;i>0;i--)
		if(v[i].in==0)
		s.push(v[i]);
	while(!s.empty()){
		cout<<s.top().vertex<<" ";
		EdgeNode*p = s.top().first;
		s.pop();
		while(p!=NULL){
			v[p->index].in--;
			if(v[p->index].in==0)
				s.push(v[p->index]);
			p = p->next;
		}
	}
}

关键路径:

struct Edge{
	int from,to,e,l;
};
class AOE{
	private:
	int edgeNum;
	int vertexNum;
	Edge*edgeset;//边集
	int edge[MaxSize][MaxSize];//邻接矩阵
	string*vertex;//点集
	public:
	AOE(int n,int e);
	~AOE(){
		
	};
	void CriPath();
};
AOE::AOE(int n,int e){
	vertexNum = n;
	edgeNum = e;
	edgeset = new Edge[edgeNum+1];
	vertex = new string[vertexNum+1];
	int w,from,to;
	for(int i = 1;i<=vertexNum;i++){
		vertex[i] = 'v'+itos(i);
		for(int j = 1;j<=vertexNum;j++){
			edge[i][j] = MaxSize;
		}
	}
	for(int i = 1;i<=edgeNum;i++){
		cin>>from>>to>>w;
		edge[from][to] = w;
		edgeset[i].from = from;
		edgeset[i].to = to;
		edgeset[i].e = 0;
		edgeset[i].l = 0;
	}
}
void AOE::CriPath(){
	int visit[vertexNum+1] = {
		0
	};
	int vl[vertexNum+1] = {
		MaxSize
	};//事件最早发生时间
	int ve[vertexNum+1] = {
		0
	};//事件最晚发生时间
	ve[1] = 0;
	queue<int>q;
	q.push(1);
	int i;
	visit[1] = 1;
	while(!q.empty()){
		i = q.front();
		q.pop();
		for(int j = 1;j<=vertexNum;j++){
			if(edge[i][j]!=MaxSize&&ve[j]<edge[i][j]+ve[i])
				ve[j] = edge[i][j]+ve[i];
			if(!visit[j])
				q.push(j);
			visit[j] = 1;
		}
	}
	q.push(vertexNum);
	for(int i = 1;i<=vertexNum;i++){
		visit[i] = 0;
		vl[i] = ve[vertexNum];
	}
	while(!q.empty()){
		i = q.front();
		//cout<<i<<" ";
		q.pop();
		for(int j = vertexNum;j>0;j--){
		//for(int j = 1;j<=vertexNum;j++){
			if(edge[j][i]!=MaxSize&&vl[j]>vl[i]-edge[j][i]){
				vl[j] = vl[i]-edge[j][i];
			}
			if(!visit[j])
				q.push(j);
			visit[j] = 1;
		}
	}
	for(int i = 1;i<=edgeNum;i++){
		edgeset[i].e = ve[edgeset[i].from];
		edgeset[i].l = vl[edgeset[i].to]-edge[edgeset[i].from][edgeset[i].to];
		if(edgeset[i].e==edgeset[i].l)
		cout<<vertex[edgeset[i].from]<<" ";//<<vertex[edgeset[i].to]<<" ";
	}
	cout<<vertex[vertexNum];
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章