图的遍历——深度优先搜索算法

类似广度优先搜索,深度优先搜索算法的定义:首先访问图G任意顶点v,并将其标记为已访问过,然后依次从v出发搜索v的每个邻接点(子节点)w。若w未曾访问过,则以w为新的出发点继续深度优先遍历,直至图中所有和源点v有路径相通的顶点均已被访问为止。若此时图中仍有未访问的顶点,则另选一个尚未访问的顶点作为新的源点重复上述过程,直至图中所有顶点均已被访问为止。

其实理解了广度优先搜素算法,理解这个也是顺带的。

对比两个算法,它们的相似之处在于最终都要扩展一个结点的所有子结点,区别在于扩展结点的过程不同,深度扩展的是E-结点的邻接结点(子结点)中的一个,并将其作为新的E-结点继续扩展,当前E-结点仍为活结点,待搜索完其子结点后,回溯到该结点扩展它的其他未搜索的邻接结点。而广度搜索,则是连续扩展E-结点的所有邻接结点(子结点)后,E-结点就成为一个死结点。深度优先搜索的思想是沿着一个方向搜到底,如果行不通,则返回来试其他的路径。

由于深度优先搜索的E-结点是分多次扩展的,所以它可以搜索到问题所有可能的解方案。但对于搜索路径的问题,不像广度优先搜索容易得到最短路径。

下面给出用邻接矩阵存储图的搜索算法:

/**
 * 深度度优先搜索算法
 * 		1.确定图的存储方式
 * 		2.设计搜索过程中的操作,其中包括为输出问题解而进行的存储操作
 * 		3.搜索到问题的解,则输出;否则回溯
 * 		4.一般在回溯前应该将顶点状态恢复为原始状态,特别是在有多解需求的问题中。
 * @author Beat IT 2018-1-30
 *
 */
	int visited[n];
	graph g[][100];
	int n;
	dfsm(int k)
	{
		int j;
		print("visited",k);
		visited[k]=1;
		//依次搜索顶点k的邻接点
		for (int j = 1; j <= n; j++) {
			if(g[k][j]=1 and visited[j]=0)//如果顶点j未被访问过,则顶点j为新出发点
				dfsm(j);//递归
				
		}
	}

深度搜索算法典型的一个应用是走迷宫问题。


此题是华为OJ上的一道题。这里我们从左上角(0,0)寻找到一条路径到达右下角(4,4),其中0是可达,1是墙。只能横着走或竖着走,不能斜着走。这里保证有唯一解,不考虑有多解的情况,即迷宫只有一条通道。

给出Java实现代码

package com.gxu.wjb;

import java.util.Stack;

/**
 * 深度度优先搜索算法
 * 		1.确定图的存储方式
 * 		2.设计搜索过程中的操作,其中包括为输出问题解而进行的存储操作
 * 		3.搜索到问题的解,则输出;否则回溯
 * 		4.一般在回溯前应该将顶点状态恢复为原始状态,特别是在有多解需求的问题中。
 * @author Beat IT 2018-1-30
 *
 */
public class DFS {
	public static void main(String[] args) {
		//定义数组存储整个地图
		int[][] map ={{0,1,0,0,0},{0,1,0,1,0},{0,0,0,0,0},{0,1,1,1,0},{0,0,0,1,0}};
	
		int[][] dir = {{1,0},{0,1}};//定义两个方向横着走或者竖着走(这里我们定义只走这两个方向,当然也可以定义多个方向)
		Stack<Node> stack = new Stack<Node>();//定义一个栈,保存路径
		int[][] visited = new int[5][5];//标记是否被访问,这个要和Map大小对应  
		Node start = new Node(0,0);//定义起始位置
		Node end = new Node(4,4);//定义终点位置
		visited[start.x][start.y] = 1;
		stack.push(start);//将起始点加入队列
		while(!stack.isEmpty())//如果队列为空了还没有找到解,说明就没有通路
		{
			boolean flag = false;//标记是否找到了一个方向
			Node pek = stack.peek();//获取栈顶元素,注意不需要出栈 
			if(pek.x==end.x&&pek.y==end.y){//如果到达目的地,则跳出循环
				break;
			}else{
				for (int i = 0; i < 2; i++) {//循环两个方向
					Node nbr = new Node(pek.x + dir[i][0],pek.y + dir[i][1]);//找到当前位置的邻居位置坐标并判断是否合法
					if(nbr.x>=0&&nbr.x<5&&nbr.y>=0&&nbr.y<5&&map[nbr.x][nbr.y]==0&&visited[nbr.x][nbr.y]==0){//判断邻居节点是否合法 
						stack.push(nbr);//合法将邻居位置加入栈     **这里是关键,也就是递归思想
						visited[nbr.x][nbr.y] = 1;//并标志为1
						flag = true;
						break;//找到了就停止循环,顺着这个方向一直搜索  
					}
					
				}
				if(flag){//找到了方向,就不用执行下面的出栈,沿着这个方向一直搜下去  
					continue;
				}
				stack.pop();//如果两个方向都不能通过,则出栈。  
			}
		}
		//这里可以加个判断,如果stack为空,说明没有解方案
		
		Stack<Node> stkRev = new Stack<Node>();//将路径反过来,因为栈中输出的路径是反的 
		while (!stack.isEmpty()) {  
            stkRev.push(stack.pop());  
        }  
        while (!stkRev.isEmpty()) {  
             System.out.println("(" + stkRev.peek().x + "," + stkRev.peek().y + ")");  
            stkRev.pop();  
        }  
	
	}
}
//定义数据结构
class Node{
	int y;
	int x;
	Node(int x,int y){
		this.x = x;
		this.y = y;
	}
}
该算法的时间复杂度是n的2次方。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值