图的遍历算法的应用

1.求一条包含所有顶点的简单路径

分析:这就是著名的哈密尔顿问题。影响找到的因素:起点的选择;顶点的邻接点次序。
在基于DFS算法中,由于起点和邻接点的选取是与顶点和邻接点的存储次序以及算法的搜索次序有关。此时可才去回溯到上一层的方法,继续查找其他的路径。
用数组patch保存当前已经搜索的简单路径上的顶点,计数器n表示当前路径上的顶点数,
【算法步骤】
(1)将计数器n的初始化为0.
(2)访问顶点时,将该顶点编号存入数组patch中,计数器n++。判断是否已经获得所求路径。如果是,则输出结果,否则继续遍历邻接点。
(3)某顶点的全部邻接顶点都访问后,任未得到简单路径,则回溯。并将该顶点置未访问,n–;
【算法实现】

//求一条包含无向图中所有顶点的简单路径——哈密尔顿问题
void Hamilton(const MGraph* G)
{
	int visited[MAX_VERTEX_NUM];//标记顶点是否被访问
	int patch[MAX_VERTEX_NUM];//存放已经搜索的简单路径上的顶点
	for (int i = 0; i < G->vexNum; i++)visited[i] = 0;
	int n=0;
	for (int i = 0, n = 0; i < G->vexNum; i++)
	{
		if (!visited[i])
			DFS(G, i, patch, visited, &n);//选择开始起点
	}
}
void DFS(const MGraph* G, int i, int patch[], int visited[], int* n)
{
	visited[i] = 1;
	patch[*n] = i; (*n)++;
	if ((*n) == G->vexNum)//符合条件,输出该简单路径
	{
		for (int j = 0; j < (*n); j++)
		{
			printf("%d ", G->vexs[patch[j]]);
		}
		printf("\n");
	}
	for (int j = 0; j < G->vexNum; j++)
	{
		if (G->arcs[i][j] && !visited[j])
			DFS(G, j, patch, visited, n);//从不同的路径上,找到每一个相邻的结点
	}
	//路径回退
	visited[i] = 0;
	(*n)--;
}

2.判断有向图中是否有环

分析:在有向图的深度遍历中,对于弧<v,w>,如果遍历出现从弧尾v开始的深度优先遍历v开始的深度优先遍历比弧w开始的优先遍历结束的早,则出现了回路。
对DFS的算法修改:
(1)对顶点vi做深度优先遍历时,对顶点vi做状态标记,遍历未开始时state为-1,遍历开始为0,遍历结束后state为1 。
(2)对图中的每一条弧<v,w>进行检查,如果弧头w未被访问则从w出发的深度优先遍历;如果w已经被访问,从弧头出发的深度优先遍历还没有结束(statew==0),而从弧尾v出发出发的深度优先遍历已经结束(statev=1),则出现了环。
【算法实现】

//判断有向图中是否存在环
//判断关键——在没有回路的情况下:先进行深度优先遍历的后结束,后进行深度优先遍历;
//在有回路的情况下:出现了  先进行深度优先遍历顶点比后进行深度优先遍历的顶点先结束
void dfs_cycle(const MGraph* G, int v, int state[], int* hasCycle)
{
	if (*hasCycle)return;//出现了环
	if (state[v] == -1)//-1表示从v开始的深度遍历还没有开始
	{
		state[v] = 0;//0表示从v开始的深度遍历已经开始
		for (int w = 0; w < G->vexNum; w++)
		{
			if (G->arcs[v][w] == 1)//从v出发的每一条弧<v,w>进行检查
			{
				if (state[w] == -1)dfs_cycle(G, w, state, hasCycle);//从w开始进行深度遍历
				else if (state[w] == 0)*hasCycle = 1;//从w开始的深度遍历还没结束
			}
		}
		state[v] = 1;//1表示从v开始的深度优先遍历结束
	}
}

3.求无向图中,从某个顶点好另一个顶点的简单路径

分析
(1)从顶点a出发到顶点i,若存在路径,则从顶点a出发进行深度优先遍历搜索,必能搜索到顶点i。
(2)由顶点a出发进行深度优先遍历已经完成的顶点(所有的邻接顶点都已经被访问过)一定不是顶点a到顶点i路径上的顶点。因此在遍历的过程中,如果该顶点的所有邻接点都被访问过,但是还没有到达目的地i。则必须删除该顶点。
【算法实现】

//求无向图的顶点a到顶点i的简单路径
void DFFSearchPath(const MGraph* G, int v, int s, int PATCH[], int* n, int visited[], int* found)
{
	//从第v个顶点出发递归地深度优先遍历图G
	//求得一条从v到s的简单路径
	visited[v - 1] = 1;//访问第v个结点
	PATCH[(*n)++] = v - 1;//将下标v加入路径中
	for (int j = 1; j < G->vexNum && !(*found); j++)
	{
		if (G->arcs[v-1][j]==1)
		{
			if (j + 1 == s)
			{
				*found = 1;
				PATCH[(*n)++] = j + 1;
			}
			else if (visited[j] == 0)
				DFFSearchPath(G, j + 1, s, PATCH, n, visited, found);
		}
	}
	if (!found)(*n)--;
}

4.求无向图的顶点vi到顶点vj的最短路径(分支数最少)

**分析:**由于广度遍历搜索访问顶点的次序是按“路径长度”的逐渐增加的次序,所以求最短路径可以基于广度优先遍历搜索进行。
修改队列的操作:
(1)队列的结点类型:prior data next
prior记录进队列的队头指针Q.front,next记录后继结点指针,data存放顶点值。
(2)进队列时,生成新的结点prior记录队头指针,next记录下一个结点的指针。
(3)出队列时,结点并不真正从队列删除,而是,改变队头指针,用Q.front记录队列的结点指针(就是上一层的结点);
【算法实现】

//求无向图中任意两点之间的最短路径算法——广度遍历算法
int BFSearchPatch(const MGraph* G, int vi, int vj, QueueBFS* Q)
{
	//初始化各个顶点的访问标志,设置为未访问
	QueueBFSInit(Q); EnQueueBFS(Q, vi);
	//设置标志数组
	int visited[MAX_VERTEX_NUM] = { 0 };
	for (int i = 0; i < G->vexNum; i++)visited[i] = 0;
	visited[vi] = 1;
	//不考虑其他的连通分量,因为所求的顶点必定与vi在同一个连通分量中
	while (!Q->front)
	{
		int v = DeQueueBFS(Q);
		for (int w = 0; w < G->vexNum; w++)
		{
			if (G->arcs[v][w] && !visited[w])
			{
				visited[w] = 1;
				EnQueueBFS(Q, w);
				if (w == vj)return 1;
			}
		}//end_for
	}//end_while
	return 0;
}
void QueueBFSInit(QueueBFS* Q)
{
	assert(Q);
	Q = (QueueBFS*)malloc(sizeof(QueueBFS));
	assert(Q);
	Q->front = Q->rear;
}
void EnQueueBFS(QueueBFS* Q, int e)
{
	assert(Q);
	QueueBFSNode* p = (QueueBFSNode*)malloc(sizeof(QueueBFSNode));
	assert(p);
	p->data = e;
	p->next = NULL;
	p->prior = Q->front;
	Q->rear->next = p;
	Q->rear = p;

}
int DeQueueBFS(QueueBFS* Q)
{
	assert(Q);
	int ret = Q->front->data;
	Q->front = Q->front->next;
	return ret;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杰深入学习计算机

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值