6.1 Graph(图)
参考博主ripplesding
本章问题
1.在图的模板中BFS和DFS与在OJ做算法题有什么不同
在写题是主要开一个标记数组,标记遍历的节点,而在图的模板中使用的结构体,是否遍历是用该节点的属性表示的。
2.拓扑排序有什么特点
实现步骤:
1.确立节点的入度和出度,将所节点入队
2.入度为0的节点出队,更新关联节点的入队,然后入度为0的节点出队
3.更新关联节点的入度
之后从第2步不断循环,如果队列能为空,那么就可以拓扑排序
图中的拓扑排序不唯一
拓扑排序存在当且仅当图为向无环图(DAG)
3.怎么在一个图上得到一个欧拉回路
首先: 确定能否得到欧拉回路,图必须满足条件,无奇度顶点且联通。
拓展: 欧拉通路,有且仅有两个奇度顶点且联通
实现步骤: 从任一顶点出发做一趟DFS,依次经历沿途经过的各边并随即将其从图中删除; 一旦有顶点度数归0,则随即将其从图中删除。每当回到起点,则得到一条欧拉子回路。此时若存在已访问但未删除的顶点,则任选其一并从中出发,再做一趟DFS,过程相同。每次所新得的子环路,都需要在搜索的起始点处与此前的子环路合并为一条更大的子环路。最终不剩任何顶点时,算法结束,当前的子环路即为原图的欧拉环路。
4.实现prim和Dijkstra算法和Kruskal算法
图的一般搜索框架:都是通过迭代逐一发现各顶点,将其纳入遍历树中,并做相应的处理。各个算法在功能上的差异,主要体现为每一步迭代中对新顶点的选取策略不同,BFS优先考查早发现的顶点,DFS优先考察最后被发现的顶点
prim算法描述:
1.在一个加权连通图中,顶点集合V
,边集合为E
2.任意选出一个点作为初始顶点,标记为visit
,加入顶点集V中,计算所有与顶点集中的点相连接的,最小距离的点,标记为visit
.
3.重复以下操作,直到所有点都被标记为visit
:
在剩下的点钟,计算与已标记visit
点距离最小的点,标记visit
,证明加入了最小生成树。
Kruskal算法描述:
基本思想:
1.先构造一个只含 n 个顶点、边集为空的子图,把子图中各个顶点看成各棵树上的根结点
2.从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,即把两棵树合成一棵树,反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。
3.依次类推,直到森林中只有一棵树,即子图中含有 n减1条边为止。
Dijkstra算法描述:
功能:计算单源最短路径
描述:
Vector存图表示 状态方程dis[i] = min(dis[i],dis[p] + mp[p][i])
邻接矩阵的状态方程 dis[start][i]=min(dis[start][i],dis[start][p] + mp[p][i])
起点到 i 点的代价,等于父节点到子节点代价的和,与当前节点的最小代价的,相比较的最小值
5.优先级遍历接口如何在prim中和Dijkstra确定节点的优先级
通过比较边的权值,来更新优先级别
6.什么是支撑树,什么是跨边(crossEdge)和树边
图G的一个支撑子图(spanning subgraph)是一个含有G的所有节点的子图。如果图G的支撑子图是一棵树,则称为G的支撑树(spanning Tree),或者称为生成树。我们通常说的最小生成树(minimal spanning tree)就是指图G的所有支撑树中边权之和最小的支撑树。
我们在做dfs的时候,当访问到一个节点时,会出现四种情况:
1.此节点未被访问过,则此次的访问关系边(发起点——>接受点)称为树边(tree edge);
2.此节点被访问过但此节点的子孙还没访问完,换句话说,此次的发起点的源头可以追溯到接收点,则此次访问关系边称为后向边(back edge);
3.此节点被访问过且此节点的子孙已经访问完,而且发起点是搜索初始边,则称为前向边(down edge);
4.此节点被访问过且此节点的子孙已经访问完,而且发起点不是搜索初始边,则称为横叉边(cross edge)。
6.1.1 ADT接口
操作接口(边) | 功能描述 | 操作接口(顶点) | 功能描述 |
---|---|---|---|
e( ) | 边总数 | n( ) | 顶点总数 |
exist(v,u) | 判断联边(v,u)是否存在 | insert(v ) | 在顶点集V中插入新的顶点v |
insert(v,u) | 引入顶点v到u的联边 | remove(v ) | 将顶点v从顶点集中删除 |
remove(v,u) | 删除从顶点v到u的联边 | inDegreee( ) outDegree( ) |
顶点的入度和出度 |
type(v,u) | 边在遍历树中所属的类型 | firstNbr(v ) | 顶点v的首个邻接节点 |
edge(v,u) | 边所对应的数据域 | nextNbr(v,u) | 在v的邻接节点中,u的后继 |
weight(v,u) | 边的权重 | status(v ) | 顶点v 的状态 |
dTime( v),fTime(v ) | 顶点v的时间标签 | ||
parent(v ) | 顶点v在遍历树中的父节点 | ||
priority(v ) | 顶点v在遍历树中的权重 |
6.1.2 Graph模板类
#include"Stack.h"
typedef enum{
UNDISCOVERED, DISCOVERED, VISITED} VStatus;
typedef enum{
UNDETERMINED, TREE, CROSS, FORWARD, BACKWARD} EStatus;
template <typename Tv, typename Te> class Graph
{
private:
/*--内部成员--*/
void reset(){
// 重置函数
for(int i = 0; i < n; i++){
status(i) = UNDISCOVERED;
dTime(i) = f(time) = -1;
parent(i) = -1; priority(i) = INT_MAX; // 优先级位最大
for(int j = 0; j < n; j++){
if(exists(i, j)) type(i, j) = UNDISCOVERED;
}
}
}
void BFS(int ,int&);//(连通域)广度搜索
void DFS(int ,int&);//深度搜索
void BCC(int ,int &,Stack<int>&);//双连通域分解
bool Tsort(int ,int &,Stack<int>&);//拓扑排序
template <typename PU> void PFS(int, PU);//优先搜索框架
public:
/*--顶点操作--*/
int n;
virtual int insert(Tv const&) = 0;
virtual Tv remove(int) = 0;
virtual Tv& vertex(int) = 0;
virtual int inDegree(int) = 0;
virtual int outDegree(int) = 0;
virtual int firstNbr(int) = 0;
virtual int nextNbr(int, int) = 0;
virtual VStatus& status(int) = 0;
virtual int& dTime(int) = 0;
virtual int& fTime(int) = 0;
virtual int& parent(int) = 0;
virtual int& priority(int) = 0;
/*--边操作--*/
int e();
virtual bool exist(int, int) = 0;
virtual void insert(int v, int u, Te const& e, int w) = 0;
virtual Te remove(int, int) = 0;
virtual EStatus& status(int, int) = 0;
virtual Te& edge(int, int) = 0;
virtual int& weight(int, int) = 0;
/*--算法--*/
void bfs(int);
void dfs(int);
template <typename PU> void pfs(int, PU)
};
6.1.3 Graph类
#pragma once
#include <assert.h>
#include <limits.h> // INT_MAX
#include "queue.h"
#include "stack.h"
namespace dtl
{
// 顶点状态
enum class VertexStatus {
// 未发现
undiscovered,
// 已发现
discovered,
// 已访问
visited,
};
// 边状态
enum class EdgeStatus {
// 未处置
undetermined,
// 树边
tree,
// 跨边
cross,
// 前向边
forward,
// 后向边
backward,
};
// class of GRAPH
template <typename Tv, typename Te>
class Graph
{
private:
// 所有顶点、边的辅助信息复位
void reset() {
for (int i = 0; i < n; i++) {
status(i) = VertexStatus::undiscovered;
dTime(i) = fTime(i) = -1;
parent(i) = -1;
priority(i) = INT_MAX;
for (int j = 0; j < n; j++) {
if (exists(i, j))
type(i, j) = EdgeStatus::undetermined;
}
}
}
// breadth first search
void BFS(int v, int& clock) {
#if DSA_MODE
printf("BFS in with v=%d, clock=%d\n", v, clock);
#endif
Queue<int> queue;
status(v) = VertexStatus::discovered;
queue.enqueue(v);
#if DSA_MODE
printf("vetex v=%d", v); print(vertex(v)); printf(" status changed to VertexStatus::discovered\n");
printf("current Q: "); print(queue);
#endif
while (!queue.empty()) {
int v = queue.dequeue();
dTime(v) = ++clock;
#if DSA_MODE
printf("visiting vertex v=%d", v); print(vertex(v)); printf("["); print(status(v)); printf("] dTime=%d\n", clock);
#endif
for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) {
#if DSA_MODE
printf("visiting vertex u=%d", u); print(vertex(u)); printf("["); print(status(u)); printf("]\n");
#endif
if (status(u) == VertexStatus::undiscovered) {
status(u) = VertexStatus::discovered;
queue.enqueue(u);
type(v, u) = EdgeStatus::tree;
parent(u) = v;
} else {
type(v, u) = EdgeStatus::cross;
}
#if DSA_MODE
printf("edge(%d, %d) ", v, u); print(vertex(v)); printf(" ->"); print(vertex(u)); printf(" now is "); print(type(v, u)); printf("\n");
#endif
}
status(v) = VertexStatus::visited;
#if DSA_MODE
printf("vetex v=%d", v); print(vertex(v)); printf(" status changed to VertexStatus::visited\n");
printf("current Q: "); print(queue);
#endif
}
}
//! depth first search
void DFS(int v, int& clock) {
#if DSA_MODE
printf("DFS in with v=%d, clock=%d\n", v, clock);
#endif
dTime(v) = ++clock;
status(v) = VertexStatus::discovered;
#if DSA_MODE
printf("vetex v=%d", v); print(vertex(v)); printf(" status changed to VertexStatus::discovered dTime=%d\n", clock);
#endif
for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) {
#if DSA_MODE
printf("visiting vertex u=%d", u); print(vertex(u)); printf("["); print(status(u)); printf("]\n");
#endif
switch (status(u)) {
case VertexStatus::undiscovered:
type(v, u) = EdgeStatus::tree;
parent(u) = v;
DFS(u, clock);
break;
case VertexStatus::discovered:
type(v, u) = EdgeStatus::backward;
break;
default: // VertexStatus::visited
type(v, u) = dTime(v) < dTime(u) ? EdgeStatus::forward : EdgeStatus::cross;
break;
}
#if DSA_MODE
printf("edge(%d, %d) ", v, u); print(vertex(v)); printf(" ->"); print(vertex(u)); printf(" now is "); print(type(v, u)); printf("\n");
#endif
}
status(v) = VertexStatus::visited;
fTime(v) = ++clock;
#if DSA_MODE
printf("vetex v=%d", v); print(vertex(v)); printf(" status changed to VertexStatus::visited fTime=%d\n", clock);
#endif
}
//! topological sort
bool TSORT(int v, int& clock, Stack<Tv>* s) {
assert(0 <= v && v < n);
#if DSA_MODE
printf("TSORT in with v=%d, clock=%d\n", v, clock);
printf("statck="); print(s);
#endif
dTime(v) = ++clock;
status(v) = VertexStatus::discovered;
#if DSA_MODE
printf("vetex v=%d", v); print(vertex(v)); printf(" status changed to VertexStatus::discovered dTime=%d\n", clock);
#endif
for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) {
#if DSA_MODE
printf("visiting vertex u=%d", u); print(vertex(u)); printf("["); print(status(u)); printf("]\n");
#endif
switch (status(u)) {
case VertexStatus::undiscovered:
parent(u) = v;
type(v, u) = EdgeStatus::tree;
#if DSA_MODE
printf("edge(%d, %d) ", v, u); print(vertex(v)); printf(" ->"); print(vertex(u)); printf(" now is "); print(type(v, u)); printf("\n");
#endif
if (!TSORT(u, clock, s)) {
return false;
}
break;
case VertexStatus::discovered:
type(v, u) = EdgeStatus::backward;
#if DSA_MODE
printf("edge(%d, %d) ", v, u); print(vertex(v)); printf(" ->"); print(vertex(u)); printf(" now is "); print(type(v, u)); printf("\n");
#endif
return false;
break;
default: // visited
type(v, u) = (dTime(v) < dTime(u)) ? EdgeStatus::forward : EdgeStatus::cross;
#if DSA_MODE
printf("edge(%d, %d) ", v, u); print(vertex(v)); printf(" ->"); print(vertex(u)); printf(" now is "); print(type(v, u)); printf("\n");
#endif
break;
}
}
status(v) = VertexStatus::visited;
s->push(vertex(v));
#if DSA_MODE
printf("vetex v=%d", v); print(vertex(v)); printf(" status changed to VertexStatus::visited\n");
#endif
return true;
}
public:
//! 顶点总数
int n;
//! 插入顶点,返回编号
virtual int insert(Tv const& v) = 0;
//! 删除顶点及其关联边,返回该顶点
virtual Tv remove(int v) = 0;
//! 顶点数据(该顶点的确存在)
virtual Tv& vertex(int v) = 0;
//! 顶点的入度(该顶点的确存在)
virtual int inDegree(int v) = 0;
//! 顶点的出度(该顶点的确存在)
virtual int outDegree(int v) = 0;
//! 顶点的首个邻接顶点
virtual int firstNbr(int v) = 0;
//! 顶点v的(相对于顶点j的)下一个邻接顶点
virtual int nextNbr(int v, int j) = 0;
//! 顶点状态
virtual VertexStatus& status(int v) = 0;
//! 顶点 discovered time
virtual int& dTime(int v) = 0;
//! 顶点 finished time
virtual int& fTime(int v) = 0;
//! 顶点在遍历树中的父亲
virtual int& parent(int v) = 0;
//! 顶点在遍历树中的优先级
virtual int& priority(int v) = 0;
//! 边总数
int e;
//! 边(v,u)是否存在
virtual bool exists(int v, int u) = 0;
//! 在顶点v、u之间插入权重为w的边e
virtual void insert(Te const& e, int w, int v , int u) = 0;
//! 的喊出顶点v、u之间的边e,返回该边信息
virtual Te remove(int v, int u) = 0;
//! 边(v,u)的数据(该边的确存在)
virtual EdgeStatus& type(int v, int u) = 0;
//! 边(v,u)的权重
virtual int& weight(int v, int u) = 0;
//********* algorithms *********************//
//! 广度优先搜索
void bfs(int s) {
reset();
int clock = 0;
int v = s;
do {
if (VertexStatus::undiscovered == status(v)) {
BFS(v, clock);
}
} while (s != (v = (++v % n)));
}
//! 深度优先搜索
void dfs(int s) {
reset();
int clock = 0;
int v = s;
do {
if (VertexStatus::undiscovered == status(v)) {
DFS(v, clock);
}
} while (s != (v = (++v % n)));
}
//! 基于DFS的拓扑排序算法
Stack<Tv>* tsort(int s) {
assert(0 <= s && s < n);
reset();
int clock = 0, v = s;
auto S = new Stack<Tv>();
do {
if (status(v) == VertexStatus::undiscovered) {
if (!TSORT(v, clock, S)) {
S->clear();
break;
}
}
} while (s != (v = (++v % n)));
return S;
}
};
}
6.1.4graphMatrix类
#pragma once
#include "graph.h"
#include "vector.h"
#include <assert.h>
namespace dtl
{
//! 顶点
template <typename T>
struct Vertex
{
T data;
int inDegree;//入度
int outDegree;//出度
VertexStatus status;//定点状态: 未发现undiscovered// 已发现discovered,// 已访问visited
//! discovered time
int dTime;
//! finished visit time
int fTime;
int parent;
int priority;//优先级
Vertex(T const& data = T(0))
: data(data)
, inDegree(0)
, outDegree(0)
, status(VertexStatus::undiscovered)
, dTime(-1)
, fTime(-1)
, parent(-1)
, priority(INT_MAX)
{
}
};
//! 边
template <typename T>
struct Edge
{
T data;//数据
int weight;//权重
EdgeStatus status;//状态
Edge(T const& data, int weight)
: data(data)
, weight(weight)
, status(EdgeStatus::undetermined)
{
}
};
//邻接矩阵
template <typename Tv, typename Te>
class GraphMatrix : public Graph<Tv, Te>
{
private:
//! vertexes
Vector<Vertex<Tv>> V;//顶点
//! edges
Vector<Vector<Edge<Te>*>> E;//边
public:
typedef Graph<Tv, Te> GraphType;
GraphMatrix() {
GraphType::n = GraphType::e = 0;
}
~GraphMatrix() {
for (int j = 0; j < GraphType::n; j++) {
for (int k = 0; k < GraphType::n; k++) {
delete E[j][k];
}
}
}
// vertex basic opration
virtual Tv& vertex(int i) {
return V[i].data; }
virtual int inDegree(int i) {
return V[i].inDegree; }
virtual int outDegree(int i) {
return V[i].outDegree; }
virtual VertexStatus& status(int i) {
return V[i].status; }
virtual int& dTime(int i) {
return V[i].dTime; }
virtual int& fTime(int i) {
return V[i].fTime; }
virtual int& parent(int i) {
return V[i].parent; }
virtual int& priority(int i) {
return V[i].priority; }
virtual int firstNbr(int i) {
return nextNbr(i, GraphType::n); }
virtual int nextNbr(int i, int j) {
while ((-1 < j) && (!exists(i, --j))) {
}
return j;
}
// vertex dynamic operation
virtual int insert(Tv const& vertex) {
for (int j = 0; j < GraphType::n; j++) {
E[j].insert(nullptr);
}
GraphType::n++;
E.insert(Vector<Edge<Te>*>(GraphType::n, GraphType::n, (Edge<Te>*)nullptr));
return V.insert(Vertex<Tv>(vertex));
}
virtual Tv remove(int i) {
assert(0 <= i && i < GraphType::n);
for (int j = 0; j < GraphType::n; j++) {
if (exists(i, j)) {
delete E[i][j];
V[j].inDegree--;
}
}
E.remove(i);
GraphType::n--;
Tv vBak = vertex(i);
V.remove(i);
for (int j = 0; j < GraphType::n; j++) {
if (Edge<Te>* e = E[j].remove(i)) {
delete e;
V[j].outDegree--;
}
}
return vBak;
}
// edge operation
virtual bool exists(int i, int j) {
return (0 <= i) && (i < GraphType::n) && (0 <= j) && (j < GraphType::n) && E[i][j] != nullptr;
}
virtual EdgeStatus& type(int i, int j) {
assert(exists(i, j)); return E[i][j]->status; }
virtual Te& edge(int i, int j) {
assert(exists(i, j)); return E[i][j]->data; }
virtual int& weight(int i, int j) {
assert(exists(i, j)); return E[i][j]->weight; }
virtual void insert(Te const& edge, int weight, int i, int j) {
if (exists(i, j)) {
return; }
E[i][j] = new Edge<Te>(edge, weight);
GraphType::e++;
V[i].outDegree++;
V[j].inDegree++;
}
virtual Te remove(int i, int j) {
assert(exists(i, j));
Te eBak = edge(i, j);
delete E[i][j];
E[i][j] = nullptr;
GraphType::e--;
V[i].outDegree--;
V[j].inDegree--;
return eBak;
}
};
}