算法导论 22.2-8 无向图遍历

一、题目

设G=(V,E)是一个连通的无向图。请给出一个O(V+E)时间的算法,以计算图G中的一条路径,对于E中的每一条 边,该路径恰好在每一个方向上遍历一次。如果你身处一个迷宫之中,说明如何才能找到出路。

二、思考

直觉就是说DFS,这一节讲的是BFS,不知道怎么用BFS处理

三、代码

1.Link_Graph.h

#include <iostream>
#include <queue>
using namespace std;

#define N 100

#define UNUSED 0
#define USED 1

queue<int> Q;
struct Vertex;
//每条边代表一场比赛,如果一端的顶点是坏的,那么另一端的顶点必须是好的
struct Edge
{
	bool IsUsed;
	int start;
	int end;
	int value;
	Edge *next;
	Edge *nextDFS;
	Edge(int s, int e, int v)
		:IsUsed(UNUSED),start(s),end(e),value(v),next(NULL),nextDFS(NULL){}
};
//每个顶点代表一个摔跤手,增加一个属性type,用于定义这个摔跤手是好的还是坏的
struct Vertex
{
	Edge *head;
	Vertex():head(NULL){};
};
class Link_Graph
{
public:
	int n;
	Vertex *V;
	Link_Graph(int num):n(num)
	{
		V = new Vertex[n+1];
	}
	~Link_Graph(){delete []V;}
	void AddSingleEdge(int start, int end, int value = 1)
	{
		Edge *NewEdge = new Edge(start, end, value);
		if(V[start].head == NULL || V[start].head->end > end)
		{
			NewEdge->next = V[start].head;
			V[start].head = NewEdge;
		}
		else
		{
			Edge *e = V[start].head, *pre = e;
			while(e != NULL && e->end < end)
			{
				pre = e;
				e = e->next;
			}
			if(e && e->end == end)
			{
				delete NewEdge;
				return;
			}
			NewEdge->next = e;
			pre->next = NewEdge;
		}
	}
	void AddDoubleEdge(int a, int b, int value = 1)
	{
		AddSingleEdge(a, b, value);
		AddSingleEdge(b, a, value);
	}
	void DeleteSingleEdge(int start, int end)
	{
		Edge *e = V[start].head, *pre = e;
		while(e && e->end < end)
		{
			pre = e;
			e = e->next;
		}
		if(e == NULL || e->end > end) return;
		if(e == V[start].head)
			V[start].head = e->next;
		else
			pre->next = e->next;
		delete e;
	}
	void DeleteDoubleEdge(int a, int b)
	{
		DeleteSingleEdge(a, b);
		DeleteSingleEdge(b, a);
	}
	//22.2-8
	bool DFS_Start(int start, int sum);
	void DFS(Edge* start, int ceil, bool &flag, int sum);
	void DFS_Init();
	void DFS_Print(Edge* start, int sum);
};
//以顶点start为起点搜索路径
bool Link_Graph::DFS_Start(int start, int sum)
{
	bool flag = false;
	//因为DFS是对边的搜索,所以要将从一个点出发的搜索转换为从一个边出发的搜索
	Edge *e = V[start].head;
	while(e)
	{
		e->IsUsed = USED;
		DFS(e, 1, flag, sum);
		if(flag == true)
		{
			DFS_Print(e, sum);
			return true;
		}
		e->IsUsed = UNUSED;
		e = e->next;
	}
	return false;
}
/*
Edge *start:当前边
int ceil:当前边是结果路径中的第几条边
flag:是否已经找到一条符合要求的路径
sum:所求路径的长度
程序的目标:找到一条以start->end为起点的边,作为结果路径的第ceil+1条边
*/
void Link_Graph::DFS(Edge *start, int ceil, bool &flag, int sum)
{
	//如果这条符合要求的路径上已经有sum条边,那么这个路径就是所求路径
	if(ceil == sum)
		flag = true;
	if(flag == true)
		return;
	//遍历以start->end为起点的边,尝试把这条边作为结果路径的第ceil+1条边,如果成功则程序返回。如果失败则尝试下一条边
	int temp = start->end;
	Edge *e = V[temp].head;
	while(e)
	{
		if(e->IsUsed == USED)
		{
			e = e->next;
			continue;
		}
		//尝试把这条边作为结果路径的第ceil+1条边
		e->IsUsed = USED;
		start->nextDFS = e;
		DFS(e, ceil+1, flag, sum);
		//如果尝试成功,则程序返回
		if(flag)
			return;
		//如果尝试失败,则先还原到尝试以前的状态,然后尝试下一条边
		int temp = start->end;
		start->nextDFS = NULL;
		e->IsUsed = UNUSED;
		e = e->next;
	}
	//没有一条符合要求的边,则失败
	flag = false;
}

void Link_Graph::DFS_Init()
{
	int i;
	Edge *e;
	for(i = 1; i <= n; i++)
	{
		e = V[i].head;
		while(e)
		{
			e->nextDFS = NULL;
			e->IsUsed = UNUSED;
			e = e->next;
		}
	}
}

void Link_Graph::DFS_Print(Edge* start, int sum)
{
	cout<< start->start;
	while(sum--)
	{
		cout<<"----->"<<start->end;
		start = start->nextDFS;
	}
	cout<<endl;
}

2.main.cpp

#include <iostream>
#include "Link_Graph.h"
using namespace std;

int main()
{
	int i = 0, a, b, n, m;
	//构造一个无向图,先输出点的个数和边的个数
	cin>>n>>m;
	Link_Graph *G = new Link_Graph(n);
	//依次输入每条边,点是从1开始计数的
	for(i = 1; i <= m; i++)
	{
		cin>>a>>b;
		//无向图
		G->AddDoubleEdge(a,b);
	}
	//算法开始
	bool ret = false;
	//依次假设每个点为起点,尝试找到一条通路
	//这里用了好几个函数,可能处理有些复杂了,但是不影响时间复杂度
	for(i = 1; i <= n; i++)
	{
		ret = G->DFS_Start(i, 2*m);
		if(ret == true)
			break;
	}
	if(ret == false)
		cout<<"No Way!"<<endl;
	delete G;
	return 0;
}


你可能感兴趣的:(算法导论 22.2-8 无向图遍历)