aspglujalk图

前言

图允许数据元素可以有多个前驱,因此可以反映数据元素之间多对多的关系


一、图的基本概念

  • 图可以分为有向图和无向图。
  • 图G=(V,E)由顶点集V(G)和关系集E(G)组成,当E(G)为空集时图G只有顶点没有边。
  • 完全图:边数达到最大的图,无向完全图的边数为n(n-1)/2;有向完全图的边数为n(n-1)
  • 权:与图的边或弧相关的数
  • 网:边或弧上带有权值的图
  • 顶点的度TD(V)

无向图:依附于顶点V的边数
有向图:等于V的入度与顶点V的出度之和,记为OD(V)
TD(V)=ID(V)+OD(V)

  • 路径

路径长度:边或弧的数目之和
回路和幻:首尾相连
简单路径:路径中不含有相同顶点
简单回路:除首尾顶点外路径不含相同顶点的回路。

  • 连通
    如果V-> V’有路径则为连通
  • 连通图

无向图:图中任意两个点都是连通的则称为无向连通图
有向图:图中任意两个点都存在双向连通路径则为强连通图

  • 连通分量

无向图中极大连通子图为连通分量
有向图中极大连通子图为强连通分量
单个结点可以自己构成一个强连通分量

  • 生成树
    设无向图G是含有N个顶点的连通图,则G的生成树含有N个结点且只有N-1条边的连通子图

n个顶点
n-1条边
连通

  • 子图

有向图

在这里插入图片描述

无向图

在这里插入图片描述

二、图的存储

1.顺序存储-邻接矩阵

数组表示法:设图G=(V,{E})有n个顶点,则G的邻接矩阵定义为n阶方阵A。
在这里插入图片描述
无向图中:顶点Vi的度等于邻接矩阵第i行非零元素的个数之和。
有向图中:顶点Vi的度等于邻接矩阵第i行和第i列非零元素的个数之和。
在这里插入图片描述

template <typename T>
class Mgraph
{
public:
	char VerterType;
	int EdgeType;

	VerterType vexs[MaxVeterNum];
	EdgeType edges[MaxVeterNum];
	int n, e;
	void creatMgraph(Mgraph *G)
};

template<typename T>
void Mgraph<T>::creatMgraph(Mgraph* G) {
	int i, j, k, w;
	scanf_s("%d%d", &G->n, &G->e);
	for ( i = 0; i <G->n; i++)
	{
		G->vexs[i] = getchar();
	}
	for ( i = 0; i < G->n; i++)
	{
		for ( j = 0; j < G->n; j++)
		{
			G->edges[i][j] = 0;
		}
	}
	for ( k = 0; k < G->e; k++)
	{
		scanf_s("%d%d", &i, &j);
		G->edges[i][j] = 1;
		G->edges[j][i] = 1;
	}
}

2.链式存储-邻接表

邻接表需要由一个头节点的顺序表+他的所有相邻节点的链表所组成

在这里插入图片描述

转载自:https://blog.csdn.net/weixin_39469127/article/details/81082019

//文件名:"GraphAdjList.h"
#pragma once
#ifndef GRAPHADJLISL_H_
#define GRAPHADJLISL_H_
 
#include <string>
#include "ObjArrayList.h"
using namespace std;
 
/*
.	图(邻接表实现) Graph Adjacency List
.	相关术语:
.		顶点 Vertex ; 边 Arc ;权 Weight ;
.		有向图 Digraph ;无向图 Undigraph ;
.		有向网 Directed Network ;无向网 Undirected Network ;
.	存储结构:
.		1.顶点表只能采用顺序结构。(因为若采用链式结构,顶点结点定义与边表结点定义就相互引用,无法定义)
.		2.边表采用链表结构。
*/
 
class GraphAdjList
{
	/*
	.	边表(链表)结点
	*/
	struct ArcNode
	{
		int adjVex;			//邻接顶点所在表中下标
		int weight;			//边权重
		ArcNode * next;		//下一条边
	};
	/*
	.	顶点表(顺序表)结点
	*/
	struct VNode
	{
		string name;		//顶点名
		ArcNode * first;	//指向的第一个依附该顶点的顶点边结点
	};
public:
	/*
	.	图 种类
	*/
	enum GraphType
	{
		DG,			//有向图,默认 0
		UDG,		//无向图,默认 1
		DN,			//有向网,默认 2
		UDN			//无向网,默认 3
	};
	/*
	.	边(弧)数据,注:供外部初始化边数据使用
	*/
	struct ArcData
	{
		string Tail;	//弧尾
		string Head;	//弧头
		int Weight;		//权重
	};
	
private:
	static const int _MAX_VERTEX_NUM = 10;		//支持最大顶点数
	
	VNode vexs[_MAX_VERTEX_NUM];				//顶点表
	int vexs_visited[_MAX_VERTEX_NUM];			//顶点访问标记数组:0|未访问 1|已访问
	int vexNum;			//顶点数
	int arcNum;			//边数
	int type;			//图种类
 
	void _CreateVexSet(ObjArrayList<string> * vexs);			//创建顶点集合
	void _CreateDG(ObjArrayList<ArcData> * arcsList);			//创建有向图
	void _CreateUDG(ObjArrayList<ArcData> * arcsList);			//创建无向图
	void _CreateDN(ObjArrayList<ArcData> * arcsList);			//创建有向网
	void _CreateUDN(ObjArrayList<ArcData> * arcsList);			//创建无向网
 
	int _Locate(string vertex);									//定位顶点元素位置
	void _InsertArc(int tail, int head, int weight);			//插入边(元操作,不分有向/无向)
	void _DeleteArc(int tail, int head);						//删除边(元操作,不分有向/无向)
	void _DFS_R(int index);										//深度优先遍历 递归
	void _DFS(int index);										//深度优先遍历 非递归
 
public:
	GraphAdjList(int type);				//构造函数:初始化图种类
	~GraphAdjList();					//析构函数
	void Init(ObjArrayList<string> * vexs, ObjArrayList<ArcData> * arcsList);		//初始化顶点、边数据为 图|网
	void InsertArc(ArcData * arcData);			//插入边(含有向/无向操作)
	void DeleteArc(ArcData * arcData);			//删除边(含有向/无向操作)
	void Display();								//显示 图|网
	void Display_DFS_R(string *vertex);		 	//从指定顶点开始,深度优先 递归 遍历
	void Display_DFS(string *vertex);			//从指定顶点开始,深度优先 非递归 遍历
	void Display_BFS(string *vertex);			//从指定顶点开始,广度优先遍历
	
};
//文件名:"GraphAdjList.cpp"
#include "stdafx.h"
#include <string>
#include "ObjArrayList.h"
#include "LinkQueue.h"
#include "LinkStack.h"
#include "GraphAdjList.h"
using namespace std;
 
GraphAdjList::GraphAdjList(int type)
{
	/*
	.	构造函数:初始化图类型
	*/
	this->type = type;
	this->vexNum = 0;
	this->arcNum = 0;
}
 
GraphAdjList::~GraphAdjList()
{
	/*
	.	析构函数:销毁图
	*/
}
 
void GraphAdjList::Init(ObjArrayList<string> * vexs, ObjArrayList<ArcData> * arcsList)
{
	/*
	.	初始化顶点、边数据,并构建 图|网
	.	入参:
	.		vexs: 顶点 列表
	.		arcsList: 边数据 列表
	*/
	//1.创建顶点集
	_CreateVexSet(vexs);
	//2.根据图类型,创建指定的图
	switch (this->type)
	{
	case DG:
		_CreateDG(arcsList); break;
	case UDG:
		_CreateUDG(arcsList); break;
	case DN:
		_CreateDN(arcsList); break;
	case UDN:
		_CreateUDN(arcsList); break;
	default:
		break;
	}
}
 
void GraphAdjList::_CreateVexSet(ObjArrayList<string> * vexs)
{
	/*
	.	创建顶点集合
	*/
	string vertex = "";
	//顶点最大数校验
	if (vexs->Length() > this->_MAX_VERTEX_NUM)
	{
		return;
	}
	//遍历顶点表,无重复插入顶点,并计数顶点数
	for (int i = 0; i < vexs->Length(); i++)
	{
		vertex = *vexs->Get(i);
		if (_Locate(vertex) == -1)
		{
			this->vexs[this->vexNum].name = vertex;
			this->vexs[this->vexNum].first = NULL;
			this->vexNum++;
		}
	}
}
 
void GraphAdjList::_CreateDG(ObjArrayList<ArcData> * arcsList)
{
	/*
	.	创建有向图
	.	邻接矩阵为 非对称边
	*/
	//初始化临时 边对象
	ArcData * arcData = NULL;
	//初始化 Tail Head 顶点下标索引
	int tail = 0, head = 0;
	//遍历边数据列表
	for (int i = 0; i < arcsList->Length(); i++)
	{
		//按序获取边(弧)
		arcData = arcsList->Get(i);
		//定位(或设置)边的两端顶点位置
		tail = _Locate(arcData->Tail);
		head = _Locate(arcData->Head);
		//插入边
		_InsertArc(tail, head, 0);
	}
}
 
void GraphAdjList::_CreateUDG(ObjArrayList<ArcData> * arcsList)
{
	/*
	.	创建无向图
	.	邻接矩阵为 对称边
	*/
	//初始化临时 边对象
	ArcData * arcData = NULL;
	//初始化 Tail Head 顶点下标索引
	int tail = 0, head = 0;
	//遍历边数据列表
	for (int i = 0; i < arcsList->Length(); i++)
	{
		//按序获取边(弧)
		arcData = arcsList->Get(i);
		//定位(或设置)边的两端顶点位置
		tail = _Locate(arcData->Tail);
		head = _Locate(arcData->Head);
		//插入对称边
		_InsertArc(tail, head, 0);
		_InsertArc(head, tail, 0);
	}
}
 
void GraphAdjList::_CreateDN(ObjArrayList<ArcData> * arcsList)
{
	/*
	.	创建有向网
	.	邻接矩阵为 非对称矩阵
	*/
	//初始化临时 边对象
	ArcData * arcData = NULL;
	//初始化 Tail Head 顶点下标索引
	int tail = 0, head = 0;
	//遍历边数据列表
	for (int i = 0; i < arcsList->Length(); i++)
	{
		//按序获取边(弧)
		arcData = arcsList->Get(i);
		//定位(或设置)边的两端顶点位置
		tail = _Locate(arcData->Tail);
		head = _Locate(arcData->Head);
		//插入边
		_InsertArc(tail, head, arcData->Weight);
	}
}
 
void GraphAdjList::_CreateUDN(ObjArrayList<ArcData> * arcsList)
{
	/*
	.	创建无向网
	.	邻接矩阵为 对称矩阵
	*/
	//初始化临时 边对象
	ArcData * arcData = NULL;
	//初始化 Tail Head 顶点下标索引
	int tail = 0, head = 0;
	//遍历边数据列表
	for (int i = 0; i < arcsList->Length(); i++)
	{
		//按序获取边(弧)
		arcData = arcsList->Get(i);
		//定位(或设置)边的两端顶点位置
		tail = _Locate(arcData->Tail);
		head = _Locate(arcData->Head);
		//插入对称边
		_InsertArc(tail, head, arcData->Weight);
		_InsertArc(head, tail, arcData->Weight);
	}
}
 
int GraphAdjList::_Locate(string vertex)
{
	/*
	.	定位顶点元素位置
	.		后期可改成【字典树】,顶点数超过100个后定位顶点位置可更快
	*/
	//遍历定位顶点位置
	for (int i = 0; i < this->_MAX_VERTEX_NUM; i++)
	{
		if (vertex == this->vexs[i].name)
		{
			return i;
		}
	}
	//cout << endl << "顶点[" << vertex << "]不存在。" << endl;
	return -1;
}
 
void GraphAdjList::_InsertArc(int tail, int head, int weight)
{
	/*
	.	插入边(元操作,不分有向/无向)
	*/
	//边结点指针:初始化为 弧尾 指向的第一个边
	ArcNode * p = this->vexs[tail].first;
	//初始化 前一边结点的指针
	ArcNode * q = NULL;
	//重复边布尔值
	bool exist = false;
	//1.边的重复性校验
	while (p != NULL)
	{
		//若已存在该边,则标记为 存在 true
		if (p->adjVex == head)
		{
			exist = true;
			break;
		}
		//若不是该边,继续下一个边校验
		q = p;
		p = p->next;
	}
	//2.1.如果边存在,则跳过,不做插入
	if (exist)
		return;
	//2.2.边不存在时,创建边
	ArcNode * newArc = new ArcNode();
	newArc->adjVex = head;
	newArc->weight = weight;
	newArc->next = NULL;
	//3.1.插入第一条边
	if (q == NULL)
	{
		this->vexs[tail].first = newArc;
	}
	//3.2.插入后序边
	else
	{
		q->next = newArc;
	}
	//4.边 计数
	this->arcNum++;
}
 
void GraphAdjList::InsertArc(ArcData * arcData)
{
	/*
	.	插入边(含有向/无向操作)
	*/
	//初始化 Tail Head 顶点下标索引
	int tail = 0, head = 0;
	tail = _Locate(arcData->Tail);
	head = _Locate(arcData->Head);
	//根据图类型,插入边
	switch (this->type)
	{
	case DG:
		_InsertArc(tail, head, 0);
		break;
	case UDG:
		_InsertArc(tail, head, 0);
		_InsertArc(head, tail, 0);
		break;
	case DN:
		_InsertArc(tail, head, arcData->Weight);
		break;
	case UDN:
		_InsertArc(tail, head, arcData->Weight);
		_InsertArc(head, tail, arcData->Weight);
		break;
	default:
		break;
	}
}
 
void GraphAdjList::_DeleteArc(int tail, int head)
{
	/*
	.	删除边(元操作,不分有向/无向)
	*/
	//边结点指针:初始化为 弧尾 指向的第一个边
	ArcNode * p = this->vexs[tail].first;
	//初始化 前一边结点的指针
	ArcNode * q = NULL;
	//1.遍历查找边
	while (p != NULL)
	{
		//若存在该边,则结束循环
		if (p->adjVex == head)
		{
			break;
		}
		//若不是该边,继续下一个边
		q = p;
		p = p->next;
	}
	//2.1.边不存在
	if (p == NULL)
	{
		cout << endl << "边[" << this->vexs[head].name << "->" << this->vexs[head].name << "]不存在。" << endl;
		return;
	}
	//2.2.边存在,删除边
	//2.2.1.若为第一条边
	if (q == NULL)
	{
		this->vexs[tail].first = p->next;
	}
	//2.2.2.非第一条边
	else
	{
		q->next = p->next;
	}
	//3.释放 p
	delete p;
}
 
void GraphAdjList::DeleteArc(ArcData * arcData)
{
	/*
	.	删除边(含有向/无向操作)
	*/
	//初始化 Tail Head 顶点下标索引
	int tail = 0, head = 0;
	tail = _Locate(arcData->Tail);
	head = _Locate(arcData->Head);
	//根据图类型,删除边
	switch (this->type)
	{
	case DG:
		_DeleteArc(tail, head);
		break;
	case UDG:
		_DeleteArc(tail, head);
		_DeleteArc(head, tail);
		break;
	case DN:
		_DeleteArc(tail, head);
		break;
	case UDN:
		_DeleteArc(tail, head);
		_DeleteArc(head, tail);
		break;
	default:
		break;
	}
}
 
void GraphAdjList::Display()
{
	/*
	.	显示 图|网
	*/
	//初始化边表结点指针
	ArcNode * p = NULL;
	cout << endl << "邻接表:" << endl;
	//遍历顶点表
	for (int i = 0; i < this->_MAX_VERTEX_NUM; i++)
	{
		//空顶点(在删除顶点的操作后会出现此类情况)
		if (this->vexs[i].name == "")
		{
			continue;
		}
		//输出顶点
		cout << "[" << i << "]" << this->vexs[i].name << " ";
		//遍历输出边顶点
		p = this->vexs[i].first;
		while (p != NULL)
		{
			cout << "[" << p->adjVex << "," << p->weight << "] ";
			p = p->next;
		}
		cout << endl;
	}
}
 
void GraphAdjList::_DFS_R(int index)
{
	/*
	.	深度优先遍历 递归
	*/
	//1.访问顶点,并标记已访问
	cout << this->vexs[index].name << " ";
	this->vexs_visited[index] = 1;
	//2.遍历访问其相邻顶点
	ArcNode * p = this->vexs[index].first;
	int adjVex = 0;
	while (p != NULL)
	{
		adjVex = p->adjVex;
		//当顶点未被访问过时,可访问
		if (this->vexs_visited[adjVex] != 1)
		{
			_DFS_R(adjVex);
		}
		p = p->next;
	}
}
 
void GraphAdjList::Display_DFS_R(string *vertex)
{
	/*
	.	从指定顶点开始,深度优先 递归 遍历
	*/
	//1.判断顶点是否存在
	int index = _Locate(*vertex);
	if (index == -1)
		return;
	//2.初始化顶点访问数组
	for (int i = 0; i < this->_MAX_VERTEX_NUM; i++)
	{
		this->vexs_visited[i] = 0;
	}
	//3.深度优先遍历 递归
	cout << "深度优先遍历(递归):(从顶点" << *vertex << "开始)" << endl;
	_DFS_R(index);
}
 
void GraphAdjList::_DFS(int index)
{
	/*
	.	深度优先遍历 非递归
	*/
	//1.访问第一个结点,并标记为 已访问
	cout << this->vexs[index].name << " ";
	vexs_visited[index] = 1;
	//初始化 边结点 栈
	LinkStack<ArcNode> * s = new LinkStack<ArcNode>();
	//初始化边结点 指针
	ArcNode * p = this->vexs[index].first;
	//2.寻找下一个(未访问的)邻接结点
	while (!s->Empty() || p != NULL)
	{
		//2.1.未访问过,则访问 (纵向遍历)
		if (vexs_visited[p->adjVex] != 1)
		{
			//访问结点,标记为访问,并将其入栈
			cout << this->vexs[p->adjVex].name << " ";
			vexs_visited[p->adjVex] = 1;
			s->Push(p);
			//指针 p 移向 此结点的第一个邻接结点
			p = this->vexs[p->adjVex].first;
		}
		//2.2.已访问过,移向下一个边结点 (横向遍历)
		else
			p = p->next;
		//3.若无邻接点,则返回上一结点层,并出栈边结点
		if (p == NULL)
		{
			p = s->Pop();
		}
	}
	//释放 栈
	delete s;
}
 
void GraphAdjList::Display_DFS(string *vertex)
{
	/*
	.	从指定顶点开始,深度优先 非递归 遍历
	*/
	//1.判断顶点是否存在
	int index = _Locate(*vertex);
	if (index == -1)
		return;
	//2.初始化顶点访问数组
	for (int i = 0; i < this->_MAX_VERTEX_NUM; i++)
	{
		this->vexs_visited[i] = 0;
	}
	//3.深度优先遍历 递归
	cout << "深度优先遍历(非递归):(从顶点" << *vertex << "开始)" << endl;
	_DFS(index);
}
 
void GraphAdjList::Display_BFS(string *vertex)
{
	/*
	.	从指定顶点开始,广度优先遍历
	*/
	//1.判断顶点是否存在
	int index = _Locate(*vertex);
	if (index == -1)
		return;
	//2.初始化顶点访问数组
	for (int i = 0; i < this->_MAX_VERTEX_NUM; i++)
	{
		this->vexs_visited[i] = 0;
	}
	//3.广度优先遍历
	cout << "广度优先遍历:(从顶点" << *vertex << "开始)" << endl;
	//3.1.初始化队列
	LinkQueue<int> * vexQ = new LinkQueue<int>();
	//3.2.访问开始顶点,并标记访问、入队
	cout << this->vexs[index].name << " ";
	this->vexs_visited[index] = 1;
	vexQ->EnQueue(new int(index));
	//3.3.出队,并遍历邻接顶点(下一层次),访问后入队
	ArcNode * p = NULL;
	int adjVex = 0;
	while (vexQ->GetHead() != NULL)
	{
		index = *vexQ->DeQueue();
		p = this->vexs[index].first;
		//遍历邻接顶点
		while (p != NULL)
		{
			adjVex = p->adjVex;
			//未访问过的邻接顶点
			if (this->vexs_visited[adjVex] != 1)
			{
				//访问顶点,并标记访问、入队
				cout << this->vexs[adjVex].name << " ";
				this->vexs_visited[adjVex] = 1;
				vexQ->EnQueue(new int(adjVex));
			}
			p = p->next;
		}
	}
 
	//4.释放队列
	int * e;
	while (vexQ->GetHead() != NULL)
	{
		e = vexQ->DeQueue();
		delete e;
	}
	delete vexQ;
}
//文件名:"GraphAdjList_Test.cpp"
#include "stdafx.h"
#include <iostream>
#include "GraphAdjList.h"
#include "ObjArrayList.h"
using namespace std;
 
int main()
{
	//初始化顶点数据
	string * v1 = new string("v1");
	string * v2 = new string("v2");
	string * v3 = new string("v3");
	string * v4 = new string("v4");
	string * v5 = new string("v5");
	string * v6 = new string("v6");
	string * v7 = new string("v7");
	ObjArrayList<string> * vexs = new ObjArrayList<string>();
	vexs->Add(v1);
	vexs->Add(v2);
	vexs->Add(v3);
	vexs->Add(v4);
	vexs->Add(v5);
	vexs->Add(v6);
	vexs->Add(v7);
 
	//初始化边(弧)数据
	GraphAdjList::ArcData * arc1 = new GraphAdjList::ArcData{ "v1", "v2", 2 };
	GraphAdjList::ArcData * arc2 = new GraphAdjList::ArcData{ "v1", "v3", 3 };
	GraphAdjList::ArcData * arc3 = new GraphAdjList::ArcData{ "v1", "v4", 4 };
	GraphAdjList::ArcData * arc4 = new GraphAdjList::ArcData{ "v3", "v1", 5 };
	GraphAdjList::ArcData * arc5 = new GraphAdjList::ArcData{ "v3", "v2", 6 };
	GraphAdjList::ArcData * arc6 = new GraphAdjList::ArcData{ "v3", "v5", 7 };
	GraphAdjList::ArcData * arc7 = new GraphAdjList::ArcData{ "v2", "v5", 8 };
	GraphAdjList::ArcData * arc8 = new GraphAdjList::ArcData{ "v4", "v6", 9 };
	GraphAdjList::ArcData * arc9 = new GraphAdjList::ArcData{ "v4", "v7", 9 };
	GraphAdjList::ArcData * arc10 = new GraphAdjList::ArcData{ "v6", "v7", 9 };
	ObjArrayList<GraphAdjList::ArcData> * arcsList = new ObjArrayList<GraphAdjList::ArcData>();
	arcsList->Add(arc1);
	arcsList->Add(arc2);
	arcsList->Add(arc3);
	arcsList->Add(arc4);
	arcsList->Add(arc5);
	arcsList->Add(arc6);
	arcsList->Add(arc7);
	arcsList->Add(arc8);
	arcsList->Add(arc9);
	arcsList->Add(arc10);
 
	//测试1:无向图
	cout << endl << "无向图初始化:" << endl;
	GraphAdjList * udg = new GraphAdjList(GraphAdjList::UDG);
	udg->Init(vexs, arcsList);
	udg->Display();
	//1.1.深度优先遍历
	cout << endl << "无向图深度优先遍历序列:(递归)" << endl;
	udg->Display_DFS_R(v1);
	cout << endl << "无向图深度优先遍历序列:(非递归)" << endl;
	udg->Display_DFS(v1);
	//1.2.广度优先遍历
	cout << endl << "无向图广度优先遍历序列:" << endl;
	udg->Display_BFS(v2);
	//1.3.插入新边
	cout << endl << "无向图新边:" << endl;
	udg->InsertArc(new GraphAdjList::ArcData{ "v7", "v1", 8 });
	udg->Display();
	//1.4.删除边
	cout << endl << "无向图删除边arc9:" << endl;
	udg->DeleteArc(arc9);
	udg->Display();
 
	//测试2:有向图
	cout << endl << "有向图:" << endl;
	GraphAdjList * dg = new GraphAdjList(GraphAdjList::DG);
	dg->Init(vexs, arcsList);
	dg->Display();
	//2.1.深度优先遍历
	cout << endl << "有向图深度优先遍历序列:(递归)" << endl;
	dg->Display_DFS_R(v1);
	cout << endl << "有向图深度优先遍历序列:(非递归)" << endl;
	dg->Display_DFS(v1);
	//2.2.广度优先遍历
	cout << endl << "有向图广度优先遍历序列:" << endl;
	dg->Display_BFS(v2);
 
	//测试:无向网
	cout << endl << "无向网:" << endl;
	GraphAdjList * udn = new GraphAdjList(GraphAdjList::UDN);
	udn->Init(vexs, arcsList);
	udn->Display();
 
	//测试:有向网
	cout << endl << "有向网:" << endl;
	GraphAdjList * dn = new GraphAdjList(GraphAdjList::DN);
	dn->Init(vexs, arcsList);
	dn->Display();
	
	return 0;
}

代码如下(示例):

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import  ssl
ssl._create_default_https_context = ssl._create_unverified_context

三.图的遍历

3.1深度优先搜索DFS

  • 访问指定的结点v,将v作为结点
  • 访问下一个未被访问的邻接点,将其作为当前顶点
  • 重复上一过程直到所有的邻接点都被访问过(此时可能并不是所有的结点都被访问过)
  • 沿搜索路径回退,退回到有邻接点尚未被访问的结点,重复2

可以用递归或者stack实现
代码在上面

3.2广度优先搜索BFS

  • 首先访问顶点V,将V作为当前顶点
  • 访问当前顶点的所有未访问过的邻接点,并将依次访问的这些邻接点作为当前顶点
  • 重复2直到所有顶点被访问
  • 可以用queue实现

四.贪心算法

贪心算法并不从整体最优考虑,每一次的贪心算法都是对当前形势上的局部最优解,我们希望这些局部最优解能够得到整体最优解。贪心算法多应用于最短路径/最小生成树,最优仓库存储管理,航空管制系统,邮递员等问题。

  • 基本步骤
  • 建立数学模型
  • 将求解的问题分为若干个子问题(有点像递归)
  • 得到子问题局部最优解
  • 得到全局解

4.1贪心算法基本要素

  1. 贪心选择性质:整体最优解可以由局部最优解得到
  2. 最优子结构性质:整体最优解包含子问题的最优解

待补充

五.最小生成树

  • prim’s算法
  • kruskal’s算法
  • 重复删除算法

5.1两个性质特点

回路与切分
在这里插入图片描述
在这里插入图片描述

5.2 Prim算法

在这里插入图片描述
在这里插入图片描述
dv表示路径的权重,pv表示最后一个影响边权重的结点。利用贪心算法的思想在每次一连接节点时都选择最短的权重去链接

5.3kruskal算法

注意:在Kruskal算法中,顶点连接之前需要先对边的权重进行排序时间为O(nlogn)
在这里插入图片描述

5.4 Prim与Kurskal性能对比

Prim算法

以连通为主,(n-1)次连接
时间复杂度与边无关为O(n2)
适合于求边稠密网的最小生成树(顶点数较少,边数较多)

Kurskal算法

以最小代价为主,添加不形成回路的最小代价边
时间复杂度与边有关为O(nlog2n)
适合于求边稀疏网的最小生成树(顶点数较多,边数较少)

六.最短路径(Dijistra)

图论学习之Dijkstra算法的总结
转载自:yanping liang

七.拓扑排序

将非线性的有向图转化为线性排序
AOV图:

顶点表示活动,弧表示活动的先后次序。
如果有回路工程就无法结束。

在这里插入图片描述

算法步骤:

  • 在图中选取一个没有前驱的结点作为顶点
  • 删除该顶点和以它为弧尾的弧
  • 重复以上两部直到图中的所有顶点都输出(得到拓扑排序的有序序列)或图中没有前驱的顶点即没有环状结构。

八.关键路径

AOE图:顶点表示事件(时间很短),弧表示活动(权表时间)。AOE只有一个入度为0的源点表示工程的开始和一个出度为0的汇点表示工程的结束。
AOE图
e.g.下图中只有在a4和a5都完成后才能到达5.
在这里插入图片描述
算法分析:

  • 工程图必须是有向无环图
  • 工期是从源点到汇点的最短完成时间,即某一条到汇点的路径最长时间是汇点开始的最早时间。
  • 利用拓扑排序来求各个事件的最早开始时间。

  • 在工期不变的情况下,求各个事件的最晚开始时间。若某些事件的最早开始时间和最晚开始时间相同,则这些事件就是关键活动上的事件,可以求出关键活动和关键路径。
  • 从汇点开始倒推每个结点的最晚开始时间。利用拓扑排序从汇点开始求每个活动开始事件的最晚时间。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值