【算法学习】图相关算法编程实现-深度优先遍历和广度优先遍历

一、图的表示

图G=(V,E)。要表示一个图,通常有两种方法:邻接表和邻接矩阵。两种方法都既可以表示有向图,也可以表示无向图。

邻接表表示由一个包含|V|个列表的数组组成,其中每个列表对应V中的一个顶点。每个邻接表中的顶点一般以任意顺序存储。

实例:


图一 无向图的邻接矩阵表示


图二 无向图的邻接表表示


图三 有向图的邻接矩阵


图四 有向图的邻接表表示


图五 带权图的邻接矩阵表示

邻接表适合表示稀疏图。所需要的存储空间是O(V+E)。

邻接矩阵所需存储空间:O(V*V)。


二、图的广度优先搜索

算法思想:

广度优先搜索假设从图中某个顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,然后再分别从这些邻接点出发依次访问它们的邻接点,并使先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问(因此需要用队列来存储顶点),直到图中所有已被访问的顶点的邻接点都被访问为止。如果此时图中还有未被访问的顶点,则另选图中未被访问的顶点作为起点,重复上述过程,直到图中所有顶点都被访问为止。


通常采用队列作为辅助结构。


图示:


图六 用于广度优先搜索的图-德国城市分布图


图七 广度优先搜索结果

注意:广度优先搜索计算出来的每个顶点到源顶点之间的距离就是最短路径距离。


时间复杂度:O(V+E)。


采用了队列辅助数据结构。每个顶点只入队列一次,也最多出队列一次。入队和出队的时间复杂度均为O(1)。因此队列操作所需的时间O(V)。当每个顶点出队时才需要扫描链表,每个顶点的邻接表只被扫描一次。所有邻接表长度为O(E),因此扫描邻接表时间复杂度为O(E)。初始化开销为O(V)。


简易代码:

 std::queue<node*> visited, unvisited; 
 node nodes[9];
 node* current;
 
 unvisited.push(&nodes[0]); //先把root放入unvisited queue
 
 while(!unvisited.empty()) //只有unvisited不空
 {
    current = (unvisited.front()); //目前應該檢驗的
 
    if(current -> left != NULL)
       unvisited.push(current -> left); //把左邊放入queue中
 
    if(current -> right != NULL) //右边压入。因为QUEUE是一个先进先出的结构,所以即使后面再压其他东西,依然会先访问这个。
       unvisited.push(current -> right);
 
    visited.push(current);
 
    cout << current -> self << endl;
 
    unvisited.pop();
 }



三、深度优先搜索


算法思想


对于最新发现的顶点,如果它还有以此起点而未探测到的边,就沿此边继续探测下去。当顶点v的所有边都已被探寻过后,搜索将回溯到发现顶点v有起始点的那些边。这个过程一直进行到已发现从源顶点可达的所有顶点时为止。如果还存在未被发现的顶点,则选择其中一个作为源顶点,并重复以上过程。

深度优先搜索的先辈子图形成一个由数棵深度优先树组成的深度优先森林。


伪代码:

DFS(G,s)
	for each vertex v in V(G)
		status[v] = WHITE
		/******其他初始化******/
	for each vertex v in V(G)
		if(status[v]==WHITE)
			DFS-VISIT(v)

DFS-VISIT(v)
	status[v] = GRAY
	for each vertex t in Adj(v)
		if status[t] = WHITE
			DFS-VISIT(t)
			/******其他操作******/
	status[v] = BLACK

时间复杂度:O(V+E)。采用聚集分析方法。

DFS中两个循环所花时间为O(V),其中不包括调用DFS-VISIT()的时间。对于每个顶点,DFS-VISIT()只被调用一次。在这个函数的一次执行过程中,其中的循环执行的次数是与当前顶点v相邻的顶点的个数。一次所有调用函数DFS-VISIT()所花时间是O(E)。

所以时间复杂度是O(V+E)。


四、编程实现

Graph.h

//图相关算法编程实现《算法导论(第二版)》P322 第22章 图的基本算法
//Date:2013-03-27
//Author:江南烟雨(E-Mail:xiajunhust@gmail.com)
#include <iostream>
#include <queue>

//图的基本算法类封装实现
//这里图用邻接表表示法(且是不带权的无向图)
template <class ElemType>
class GraphClass{
public:
	//图邻接表表示中节点数据结构
	typedef struct StructGraphNode{
		ElemType elem;
		struct StructGraphNode *next;
	}GraphNode,*GraphNodeLink;

	//图遍历时节点颜色标记
	enum NodeColor{
		WHITE,//未被发现
		GRAY,//已被发现但是未访问
		BLACK//已被访问
	};

	static const int MaxVal = 99999;

	GraphClass();//默认构造函数
	~GraphClass();//析构函数
	//依据图中包含的节点以及邻接矩阵创建邻接链表表示的图
	void createGraph(ElemType *a,int n,char *matrix);
	void BFSGraph();//图的广度优先搜索
	void DFSGraph();//图的深度优先搜索

	void __printAdjacencyList();//输出图的当前邻接表

private:
	GraphNodeLink *root;//邻接表,包含了对应于每个节点的列表
	int num_nodes;//图中节点个数

	int __getNodeIndex(GraphNodeLink node);//得到某个顶点在颜色等数组中的索引
	void __DFSSubGraph(GraphNodeLink u,int &time,int *d,int *f,enum NodeColor *color,GraphNodeLink *parent);//从某个子节点开始深度优先遍历
	//删除邻接表所占空间
	void __deleteAdjacencyList();
	void __deleteSingleLinkList(GraphNodeLink head);//删除一个单链表
};

template <class ElemType>
GraphClass<ElemType>::GraphClass()
{
	root = NULL;
}

//析构函数
template <class ElemType>
GraphClass<ElemType>::~GraphClass()
{
	__deleteAdjacencyList();
}

//函数:依据图的邻接矩阵表示创建图的临界表表示
//参数:
//matrix:图的邻接矩阵,行优先,以一维数组表示
template <class ElemType>
void GraphClass<ElemType>::createGraph(ElemType *a,int n,char *matrix)
{
	num_nodes = n;
	root = new GraphNodeLink[n];
	for (int i = 0;i < n;++i)
		root[i] = NULL;

	//创建邻接表中的每个列表,每个列表对应一个顶点
	for (int i = 0;i < n;++i)
	{
		root[i] = new GraphNode;
		root[i]->elem = *(a + i);
		root[i]->next = NULL;
		GraphNodeLink loopNode = root[i];
		for (int j = 0;j < n;++j)
		{
			if (*(matrix + i * n + j) == 1)
			{
				GraphNodeLink newNode = new GraphNode;
				newNode->elem = *(a + j);
				newNode->next = NULL;
				//寻找插入的正确位置
				while(loopNode->next != NULL)
					loopNode = loopNode->next;
				loopNode->next = newNode;
			}
		}
	}
}

//图的广度优先遍历
template <class ElemType>
void GraphClass<ElemType>::BFSGraph()
{
	if (NULL == root)
	{
		cout << "The graph is empty!" << endl;
		return;
	}

	cout << "BFS :" << endl;
	//标记每个顶点的颜色,表示是否被访问过、被发现
	enum NodeColor *color = new enum NodeColor[num_nodes];
	//记录遍历时源顶点到其他顶点的距离
	int *d = new int[num_nodes];
	//记录每个顶点的父节点
	GraphNodeLink *parentNode = new GraphNodeLink[num_nodes];
	for(int i = 0;i < num_nodes;++i)
	{
		*(color + i) = WHITE;
		*(d + i) = MaxVal;
		*(parentNode + i) = NULL;
	}

	//从源顶点(邻接表中第一个列表首节点开始遍历)
	int index = __getNodeIndex(*(root + 0));
	*(color + index) = GRAY;
	*(d + index) = 0;
	*(parentNode + index) = NULL;

	std::queue<GraphNodeLink> BFSQueue;//辅助数据结构:队列
	BFSQueue.push(*(root + 0));//源节点入队列
	while(!BFSQueue.empty())
	{
		GraphNodeLink tempNode = BFSQueue.front();
		cout << tempNode->elem << " ";
		BFSQueue.pop();
		int tempIndex = __getNodeIndex(tempNode);
		*(color + tempIndex) = BLACK;
		GraphNodeLink loopNode = (*(root + tempIndex))->next;//找到邻接表中对应的列表
		while(loopNode)
		{
			int index = __getNodeIndex(loopNode);
			if (WHITE == *(color + index))//当前节点未被发现
			{
				*(d + index) = *(d + tempIndex) + 1;
				*(parentNode + index) = tempNode;
				*(color + index) = GRAY;
				BFSQueue.push(loopNode);
			}
			loopNode = loopNode->next;
		}
	}

	cout << endl;
	cout << "distance from the source node : " << endl;
	for(int i = 0;i < num_nodes;++i)
	{
		if(MaxVal == *(d + i))
			cout << "The node cannot be visited from the source node!" << endl;
		else
		cout << "node " << (*(root + i))->elem << " has distance :" << *(d + i) << " from the source node"<< endl;
	}
}

//图的深度优先遍历
template <class ElemType>
void GraphClass<ElemType>::DFSGraph()
{
	if (NULL == root)
	{
		cout << "The graph is empty!" << endl;
		return;
	}

	cout << "DFS :" << endl;
	//标记每个顶点的颜色,表示是否被访问过、被发现
	enum NodeColor *color = new enum NodeColor[num_nodes];
	//记录每个顶点的父节点
	GraphNodeLink *parentNode = new GraphNodeLink[num_nodes];
	//时间戳:顶点第一次被发现的时间以及被访问的时间
	int *d = new int[num_nodes];
	int *f = new int[num_nodes];
	for(int i = 0;i < num_nodes;++i)
	{
		*(color + i) = WHITE;
		*(parentNode + i) = NULL;
	}

	int time = 0;//标记访问时间戳
	//从图中未被发现的节点开始,调用深度优先搜索函数
	for (int i = 0;i < num_nodes;++i)
	{
		GraphNodeLink currentNode = *(root + i);
		while(currentNode)
		{
			int tempIndex = __getNodeIndex(currentNode);
			if(WHITE == *(color + tempIndex))
				__DFSSubGraph(currentNode,time,d,f,color,parentNode);

			currentNode = currentNode->next;
		}
	}

	cout << endl;

	cout << "time of nodes :(first find the node,end of checking) " << endl;
	for (int i = 0;i < num_nodes;++i)
	{
		cout << "(" << *(d + i) << ", " << *(f + i) << ")  ";
	}
	cout << endl;
}

//从某个子节点开始深度优先搜索
template <class ElemType>
void GraphClass<ElemType>::__DFSSubGraph(typename GraphClass<ElemType>::GraphNodeLink u,int &time,int *d,int *f,
										 enum NodeColor *color,typename GraphClass<ElemType>::GraphNodeLink *parent)
{
	cout << u->elem << " ";
	++time;
	int currentIndex = __getNodeIndex(u);
	*(color + currentIndex) = GRAY;
	*(d + currentIndex) = time;
	GraphNodeLink loopNodeLink = (*(root + currentIndex))->next;
	while(loopNodeLink)
	{
		int tempIndex = __getNodeIndex(loopNodeLink);
		//与当前节点相邻的节点未被发现
		if (WHITE == *(color + tempIndex))
		{
			*(parent + tempIndex) = u;
			__DFSSubGraph(loopNodeLink,time,d,f,color,parent);
		}
		loopNodeLink = loopNodeLink->next;
	}
	
	*(color + currentIndex) = BLACK;
	*(f + currentIndex) = ++time;
}

template <class ElemType>
int GraphClass<ElemType>::__getNodeIndex(GraphNodeLink node)
{
	for (int i = 0;i <num_nodes;++i)
	{
		if((*(root + i))->elem == node->elem)
			return i;
	}

	return -1;
}

template <class ElemType>
void GraphClass<ElemType>::__printAdjacencyList()
{
	for (int i = 0;i < num_nodes;++i)
	{
		GraphNodeLink loopNode = *(root + i);
		while(loopNode)
		{
			cout << loopNode->elem << " ";
			loopNode = loopNode->next;
		}
		cout << endl;
	}
}

//空间释放:删除邻接表所占空间
template <class ElemType>
void GraphClass<ElemType>::__deleteAdjacencyList()
{
	if(NULL == root)
		return;
	for(int i = 0;i < num_nodes;++i)
	{
		GraphNodeLink head= *(root + i);
		if(head)
			__deleteSingleLinkList(head);
	}
}

//删除一个单链表所占空间
template <class ElemType>
void GraphClass<ElemType>::__deleteSingleLinkList(typename GraphClass<ElemType>::GraphNodeLink head)
{
	if(NULL == head)
		return;

	__deleteSingleLinkList(head->next);
	delete head;
}



Graph.cpp

#include "Graph.h"

using namespace std;

int main()
{
	//《算法导论(第二版)》P322 图22-1无向图测试例子
	//const int n = 5;
	//int a[n] = {1,2,3,4,5};
	//char matrix[n * n] = {0,1,0,0,1,
	//								1,0,1,1,1,
	//								0,1,0,1,0,
	//								0,1,1,0,1,
	//								1,1,0,1,0};

	//《算法导论(第二版)》P322 图22-2有向图测试例子
	const int n = 6;
	int a[n] = {1,2,3,4,5,6};
	char matrix[n * n] = {0,1,0,1,0,0,
									0,0,0,0,1,0,
									0,0,0,0,1,1,
									0,1,0,0,0,0,
									0,0,0,1,0,0,
									0,0,0,0,0,1};

	GraphClass<int> *graphObj = new GraphClass<int>;
	graphObj->createGraph(a,n,matrix);
	graphObj->__printAdjacencyList();
	graphObj->BFSGraph();
	graphObj->DFSGraph();

	return 0;
}

运行结果:



  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
深度优先遍历广度优先遍历算法论中的基础算法,C语言可以通过邻接矩阵或邻接表实现。下面是基于邻接矩阵实现的代码: ```c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define MAX_VERTEX_NUM 100 // 最大顶点数 #define INFINITY 65535 // 表示无穷大 typedef struct { int vexs[MAX_VERTEX_NUM]; // 存储顶点的数组 int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; // 存储边的二维数组 int vexnum, arcnum; // 存储顶点数和边数 } Graph; bool visited[MAX_VERTEX_NUM]; // 存储节点是否被访问过 // 创建 void CreateGraph(Graph *G) { int i, j, k, w; printf("请输入顶点数和边数:"); scanf("%d %d", &G->vexnum, &G->arcnum); // 初始化邻接矩阵 for (i = 0; i < G->vexnum; i++) { for (j = 0; j < G->vexnum; j++) { G->arcs[i][j] = INFINITY; } } // 输入顶点 printf("请输入%d个顶点:", G->vexnum); for (i = 0; i < G->vexnum; i++) { scanf("%d", &G->vexs[i]); } // 输入边 printf("请输入%d条边:\n", G->arcnum); for (k = 0; k < G->arcnum; k++) { printf("请输入第%d条边的起点、终点和权值:", k + 1); scanf("%d %d %d", &i, &j, &w); G->arcs[i][j] = w; G->arcs[j][i] = w; // 无向,两个方向都要存储 } } // 深度优先遍历 void DFS(Graph G, int v) { int i; visited[v] = true; printf("%d ", G.vexs[v]); for (i = 0; i < G.vexnum; i++) { if (G.arcs[v][i] != INFINITY && !visited[i]) { DFS(G, i); } } } // 广度优先遍历 void BFS(Graph G, int v) { int i, j; int queue[MAX_VERTEX_NUM], front = 0, rear = 0; // 定义队列 visited[v] = true; printf("%d ", G.vexs[v]); queue[rear++] = v; while (front != rear) { i = queue[front++]; for (j = 0; j < G.vexnum; j++) { if (G.arcs[i][j] != INFINITY && !visited[j]) { visited[j] = true; printf("%d ", G.vexs[j]); queue[rear++] = j; } } } } int main() { Graph G; int i; CreateGraph(&G); // 初始化visited数组 for (i = 0; i < G.vexnum; i++) { visited[i] = false; } printf("深度优先遍历:"); for (i = 0; i < G.vexnum; i++) { if (!visited[i]) { DFS(G, i); } } // 重新初始化visited数组 for (i = 0; i < G.vexnum; i++) { visited[i] = false; } printf("\n广度优先遍历:"); for (i = 0; i < G.vexnum; i++) { if (!visited[i]) { BFS(G, i); } } return 0; } ``` 这段代码实现了基于邻接矩阵的深度优先遍历广度优先遍历算法。其中,`CreateGraph`函数用于创建,`DFS`函数用于深度优先遍历,`BFS`函数用于广度优先遍历。在遍历过程中,首先需要初始化`visited`数组,然后对于每个未被访问过的节点,调用相应的遍历函数进行遍历。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

没有昵称阿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值