c++ 学习笔记(39) 图的遍历—深度、广度优先搜索

刚拿到图这一块时,也是无从下手,因为它涉及到邻接点,和弧,还有种类,点数,边数,等等情况,但是如果你把前面的数据关系理清楚之后,问题就一切迎刃而解了:

1. 首先图中因为有点是否遍历到了,是否还未遍历到,而且其中每个点还有数据,并且每个点必须指出邻接关系,对于点的数据类型我定义为:

struct SVertexNode   //节点信息
	{
		bool bIsVisited;
		string data;
		vector<int> vecLoc; //弧的令一个节点的存储
		SVertexNode():bIsVisited(false)
		{}
	};

其中vector容器用来存储邻接点信息的。

2. 对于弧,需要知道弧的两个端点,这样一个弧就得知了。并且对于一个图,其实就是存储一些点的信息,点之间是否相连等信息。

	typedef struct SEdge  //边界信息
	{
		int iInitialNode;

		int iTerminalNode;
	}Edge;
	
	int iVertexNum;        //顶点数目 
	int iEdgeNum;          //边数目
	vector<SVertexNode> vecVertex;  //存储顶点的容器

3.有了这些其他的一些函数方法什么的,都很容易写出来了。

深度优先搜索我自己写的时候学习体会: 深度优先遍历是纵向进行的,

例如对图

   

从0开始遍历的时候,如果先遍历的是3,则会沿着3一直向它的深层进行遍历。然后再会是另一个邻接的情况。所以遍历顺序为顺序为  0 -> 3 -> 1 -> 2 (v1->v4->v2->v3)

在遍历时,1)从节点数的开始(即0)来进行(可以自己写为想要的那个点开始)

2) 遍历开始后因为前面的节点结构体中我们已经将bIsVisited 设为false, 所以在这里我们没有重新写(应该写上的)

3)对于遍历的点我们检测其邻接点,并对邻接点进行递归遍历,判断依据看看是否已经遍历过了,通过bIsVisited来进行判定

实现:

	/*
	*  brief       depth first search
	*/
	void depthFirstSearch()
	{
		cout << "depthFirstSearch: " << endl;
		for(int i = 0; i < iVertexNum; i++)
		{
			if(!vecVertex.at(i).bIsVisited)
			{
				depthFirstSearch(i);
			}
		}
	}

	void depthFirstSearch(int v)
	{
		int iAdjacent = 0;
		SVertexNode node = vecVertex.at(v);

		vecVertex.at(v).bIsVisited = true;
		cout << node.data << endl; 
		//thus we can search the node
		for(int i = 0; i < node.vecLoc.size(); i++)
		{
			iAdjacent =  node.vecLoc.at(i);

			if(!(vecVertex.at(iAdjacent).bIsVisited))
				depthFirstSearch(iAdjacent);
		}
	}

4. 广度优先遍历:即横向进行遍历的。

同样对于上图:


这时如果同样从0开始然后到3,因为是横向进行的,即先遍历同一级上的点,深度的属于下一级,这时的遍历顺序为0->3->2->1(v1->v4->v3->v2)

遍历过程参考《数据结构》严蔚敏

1)最重要解决好横向遍历的问题,如果递归时,总会是向下进行的,一直是第一个遍历了。

2)利用一个队列,先入后出,首先将所有的标志写为false, 从头进行遍历,之后对第一个进行(即0);

3)将这个进行检测的是否遍历过,若没有,则输出其数据,并将这个节点列数队列中(很经典啊)

4) 这个队列这是不为空,然后将它取出来,查找这个开始头节点的邻接点,对每一个邻接点进行遍历,输出其数据,并同时将这些邻接点列入队列中,这样队列中的头此时就是第一个访问的邻接点,再次读取队列头,这样循环读取队列头,就得到的是每一层一层的。

实现:

	/*
	*  brief     bread first search with the unjoin sort, use a queue make the search simple
	*/

	//问题:横向循环问题。
	void breadFirstSearch()
	{
		int count = 0;
		queue<int> bfs_queue;

		cout << "breadFirstSearch: " << endl;
		for(int i = 0; i < iVertexNum; i++)
		{
			vecVertex.at(i).bIsVisited = false;
		}
		for(int i = 0; i < iVertexNum; i++)
		{
			if(!(vecVertex.at(i).bIsVisited))
			{
				vecVertex.at(i).bIsVisited = true;
				cout << vecVertex.at(i).data << endl;
			}
			bfs_queue.push(i);

			while(!bfs_queue.empty())    //利用了一个队列很巧妙的实现了。
			{
				SVertexNode node = vecVertex.at(bfs_queue.front());
				bfs_queue.pop();
				for(int i = 0; i < node.vecLoc.size(); i++)
					if(!(vecVertex.at(node.vecLoc.at(i)).bIsVisited))
					{
						vecVertex.at(node.vecLoc.at(i)).bIsVisited = true;
						cout << vecVertex.at(node.vecLoc.at(i)).data << endl;
						bfs_queue.push(node.vecLoc.at(i));
					} // end if
			} // end while

		} // end for
	} // end breadFirstSearch

整体结构的代码:

#include "iostream"
#include "vector"
#include "queue"
#include "string"
using namespace std;
#define Max 26

class Graph
{
private:
	struct SVertexNode   //节点信息
	{
		bool bIsVisited;
		string data;
		vector<int> vecLoc; //弧的令一个节点的存储
		SVertexNode():bIsVisited(false)
		{}
	};

	typedef struct SEdge  //边界信息
	{
		int iInitialNode;

		int iTerminalNode;
	}Edge;
	
	int iVertexNum;        //顶点数目 
	int iEdgeNum;          //边数目
	vector<SVertexNode> vecVertex;  //存储顶点的容器

public:
	/*
	*  brief       Initalize the graph.
	*    
	*          v: vertex number of the graph.
	*/
	Graph(int v):iVertexNum(v), iEdgeNum(0)   //初始化
	{
		char szData[6];
		SVertexNode node;

		for(int i = 0; i < v; i++)
		{
			sprintf(szData, "v%d", i+1);
			node.data = szData;      //有几个节点的话先进性分配
			vecVertex.push_back(node);
		}
	}
	
	/*
	*  brief      Make an edge by initial node and terminal node.
	*/
	Edge MakeEdge(int v, int w)
	{
		Edge edge;
		edge.iInitialNode = v;
		edge.iTerminalNode = w;
		iEdgeNum++;

		return edge;
	}

	/*
	*  brief      Insert an edge to the graph
	*/
	void InsetEdge(const Edge &e)
	{
		vecVertex.at(e.iInitialNode).vecLoc.push_back(e.iTerminalNode);
		
		//if the graph is Undigraph, need do something here...
		//vecVertex.at(e.iTerminalNode).vecLoc.push_back(e.iInitialNode);

		iEdgeNum++;
	}

	/*
	*  brief       Show the graph.
	*/    
	void ShowGraph()
	{
		cout << "Show the graph" << endl;

		for(int i = 0; i < iVertexNum; i++)
		{
			cout << "Node " << i << "(" << vecVertex.at(i).data << ")";
			
			for(int j = 0; j < vecVertex.at(i).vecLoc.size(); j++)
			{
				cout << "->" << vecVertex.at(i).vecLoc.at(j);
			}
			cout << endl;
		}
	}

	/*
	*  brief       depth first search
	*/
	void depthFirstSearch()
	{
		cout << "depthFirstSearch: " << endl;
		for(int i = 0; i < iVertexNum; i++)
		{
			if(!vecVertex.at(i).bIsVisited)
			{
				depthFirstSearch(i);
			}
		}
	}

	void depthFirstSearch(int v)
	{
		int iAdjacent = 0;
		SVertexNode node = vecVertex.at(v);

		vecVertex.at(v).bIsVisited = true;
		cout << node.data << endl; 
		//thus we can search the node
		for(int i = 0; i < node.vecLoc.size(); i++)
		{
			iAdjacent =  node.vecLoc.at(i);

			if(!(vecVertex.at(iAdjacent).bIsVisited))
				depthFirstSearch(iAdjacent);
		}
	}

	/*
	*  brief     bread first search with the unjoin sort, use a queue make the search simple
	*/

	//问题:横向循环问题。
	void breadFirstSearch()
	{
		int count = 0;
		queue<int> bfs_queue;

		cout << "breadFirstSearch: " << endl;
		for(int i = 0; i < iVertexNum; i++)
		{
			vecVertex.at(i).bIsVisited = false;
		}
		for(int i = 0; i < iVertexNum; i++)
		{
			if(!(vecVertex.at(i).bIsVisited))
			{
				vecVertex.at(i).bIsVisited = true;
				cout << vecVertex.at(i).data << endl;
			}
			bfs_queue.push(i);

			while(!bfs_queue.empty())    //利用了一个队列很巧妙的实现了。
			{
				SVertexNode node = vecVertex.at(bfs_queue.front());
				bfs_queue.pop();
				for(int i = 0; i < node.vecLoc.size(); i++)
					if(!(vecVertex.at(node.vecLoc.at(i)).bIsVisited))
					{
						vecVertex.at(node.vecLoc.at(i)).bIsVisited = true;
						cout << vecVertex.at(node.vecLoc.at(i)).data << endl;
						bfs_queue.push(node.vecLoc.at(i));
					} // end if
			} // end while

		} // end for
	} // end breadFirstSearch

}; //class Graph

/*
*  brief    main for test the class of Graph and use the depthFirstSearch to search
*/
int main()
{
	Graph graph(4);
	graph.InsetEdge(graph.MakeEdge(0, 3));
	graph.InsetEdge(graph.MakeEdge(0, 2));
	graph.InsetEdge(graph.MakeEdge(1, 0));
	graph.InsetEdge(graph.MakeEdge(3, 1));
	graph.InsetEdge(graph.MakeEdge(3, 0));

	graph.ShowGraph();

	graph.depthFirstSearch();

	graph.breadFirstSearch();

	cin.get();
	cin.get();
	return 0;
}


体会: 1)对于每个结构体中的数据,其初值化的位置,以及在后续调用中的设置问题;

2)代码要将思想写好,多练习,多写,可以参考,但不能一直参考;

3)代码中的细节要把握好,比如每一个方法的简短说明,方法的命名等问题。




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值