深度优先算法(DFS) js Demo

展示效果如下图:

DFS算法就像老鼠走迷宫:朝着一个方向走到不能走,然后转向继续走,若3个方向都是是障碍,那么回到上一个位置转向···,直到找到出口,或者走完所有可能的路未找到出口。深度优先算法属于盲目搜索,无法保证搜索到的路径为最短路径。

算法实现基本思路为:把所有可能的路径看做一个从根节点出发的树形结构,从出发点开始,依次递归考察每个节点的连通性,直到找到终点或者搜索完整棵树。

核心算法:

function Position(x,y,prePOS){
	this.X=x;
	this.Y=y;
	this.PrePOS = prePOS;
}
Position.prototype.validate = function(nextPos){
	//1 在方格范围之内的,2 非障碍物,3 若当前pos的previous pos存在且,next pos不为previous pos
	if( nextPos.X >=0 && nextPos.X < X && nextPos.Y >=0 && nextPos.Y < Y && grid[nextPos.X][nextPos.Y]==1 && !searchedQ.has(nextPos)){
		if( this.PrePOS && nextPos.X == this.PrePOS.X && nextPos.Y == this.PrePOS.Y){
			return false;
		}
		return true;
	}
	return false;
}
Position.prototype.Down = function(){
	var pos = new Position(this.X + 1, this.Y);
	if(this.validate(pos)){
		pos.PrePOS = this;
		return pos;
	}
	return undefined;
};
Position.prototype.Right = function(){
	var pos = new Position(this.X, this.Y + 1);
	if(this.validate(pos)){
		pos.PrePOS = this;
		return pos;
	}
	return undefined;
};
Position.prototype.Up = function(){
	var pos = new Position(this.X - 1, this.Y);
	if(this.validate(pos)){
		pos.PrePOS = this;
		return pos;
	}
	return undefined;
};
Position.prototype.Left = function(){
	var pos =  new Position(this.X, this.Y - 1);
	if(this.validate(pos)){
		pos.PrePOS = this;
		return pos;
	}
	return undefined;
};

function Queue(){
	var me = this;
	var _list = [];
	this.length = function(){
		return _list.length;
	};
	this.push=function(position){
		if(startPos.constructor.name != "Position")
			throw "Should be Position object.";
		_list.push(position);
		return me;
	}
	this.fetch=function(){
		return _list.shift();
	}
	this.pop=function(){
		return _list.pop();
	}
	this.has=function(position){
		for(var i=0,len=_list.length;i<len;i++){
			if(_list[i].X == position.X && _list[i].Y == position.Y){
				return _list[i];
			}
		}
		return undefined;
	}
	this.Item = _list;
}

function searchPath(POS){
	if(!POS){
		return false;
	}
	searchCount++;
	searchedQ.push(POS);
	if((POS.X == endPos.X && POS.Y == endPos.Y)){
		return true;
	}else{
		var found = false;
		var down = POS.Down();
		var right = POS.Right();
		var up = POS.Up();
		var left = POS.Left();
		
		if(!found && down){
			found = searchPath(down)
		}
		if(!found && right) {
			found = searchPath(right);
		}
		if(!found && up) {
			found = searchPath(up);
		}
		if(!found && left) {
			found = searchPath(left);
		}
		if(!found){
			grid[POS.X][POS.Y] = -1;
			return searchPath(POS.PrePOS);
		}else{
			return true;
		}	
	}
}


以下为实现代码,仅供参考(算法还需要完善,如果有发现错误,请批评指正,谢谢)

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />

</head>
<body οncοntextmenu="return false" onselectstart="return false">
格子长:<input type="text" id="txtLength" value="10" /> 格子宽:<input type="text" id="txtWidth" value="10"/> 
<input type="button" value="绘制格子" id="btnPrint" οnclick="initGrid();" />
<input type="button" value="搜索路径" id="btnPrint" οnclick="startSearch();" />
<br/>
提示:点击格子设置/取消障碍
<div id="container">
</div>
<div id="msg">
</div>
<div>
<script>
var tdTitle = "点击格子设置/取消障碍";
var grid = [];
var X = Y = 0;
var gridUI = [];
var pathLength = 0;
var searchedQ = new Queue();
var searchCount = 0;
var startPos = new Position(0,0), endPos = new Position(0,0);

initGrid();
function $(id) {
	return document.getElementById(id);
}
function initGrid(){
	pathLength = 0;
	closedQ = new Queue();
	X = parseInt($("txtLength").value) || 0;
	Y = parseInt($("txtWidth").value) || 0;
	var html = "<table id='mainTable' border='0' cellpadding='1' cellspacing='1' style='background:#999999;' ><tbody>";
	for(var i=0;i<X;i++){
		html += "<tr>";
		grid[i] = [];
		for(var j=0;j<Y;j++){
			grid[i][j] = 1;
			html += "<td title='"+ tdTitle +"' width='20' height='20' style='background:#ffffff;' οnclick='setBlock(this,"+ i +","+ j +")'> </td>";
		}
		html += "</tr>";
	}
	html += "</tbody></table>";
	$("container").innerHTML = html;
	
	var table = $('mainTable');
	for (var i = 0; i < X; i++) {
		gridUI[i] = new Array(Y);
		for (var j = 0; j < Y; j++) {
			gridUI[i][j] = table.rows[i].cells[j];
		}
	}
	
	//print start and end position.
	endPos = new Position(X-1,Y-1);
	gridUI[startPos.X][startPos.Y].style.background='#339933';
	gridUI[endPos.X][endPos.Y].style.background='#339933';
	gridUI[startPos.X][startPos.Y].οnclick="return false;";
	gridUI[endPos.X][endPos.Y].οnclick="return false;";
	gridUI[startPos.X][startPos.Y].title="start";
	gridUI[endPos.X][endPos.Y].title="end";
}

function setBlock(td,i,j){
	if(grid[i][j] == 1){
		gridUI[i][j].style.background='#666666';
		grid[i][j] = 0;//block
	}else{
		gridUI[i][j].style.background='#ffffff';
		grid[i][j] = 1;
	}
}

function Position(x,y,prePOS){
	this.X=x;
	this.Y=y;
	this.PrePOS = prePOS;
}
Position.prototype.validate = function(nextPos){
	//1 在方格范围之内的,2 非障碍物,3 若当前pos的previous pos存在且,next pos不为previous pos
	if( nextPos.X >=0 && nextPos.X < X && nextPos.Y >=0 && nextPos.Y < Y && grid[nextPos.X][nextPos.Y]==1 && !searchedQ.has(nextPos)){
		if( this.PrePOS && nextPos.X == this.PrePOS.X && nextPos.Y == this.PrePOS.Y){
			return false;
		}
		return true;
	}
	return false;
}
Position.prototype.Down = function(){
	var pos = new Position(this.X + 1, this.Y);
	if(this.validate(pos)){
		pos.PrePOS = this;
		return pos;
	}
	return undefined;
};
Position.prototype.Right = function(){
	var pos = new Position(this.X, this.Y + 1);
	if(this.validate(pos)){
		pos.PrePOS = this;
		return pos;
	}
	return undefined;
};
Position.prototype.Up = function(){
	var pos = new Position(this.X - 1, this.Y);
	if(this.validate(pos)){
		pos.PrePOS = this;
		return pos;
	}
	return undefined;
};
Position.prototype.Left = function(){
	var pos =  new Position(this.X, this.Y - 1);
	if(this.validate(pos)){
		pos.PrePOS = this;
		return pos;
	}
	return undefined;
};

function Queue(){
	var me = this;
	var _list = [];
	this.length = function(){
		return _list.length;
	};
	this.push=function(position){
		if(startPos.constructor.name != "Position")
			throw "Should be Position object.";
		_list.push(position);
		return me;
	}
	this.fetch=function(){
		return _list.shift();
	}
	this.pop=function(){
		return _list.pop();
	}
	this.has=function(position){
		for(var i=0,len=_list.length;i<len;i++){
			if(_list[i].X == position.X && _list[i].Y == position.Y){
				return _list[i];
			}
		}
		return undefined;
	}
	this.Item = _list;
}

function searchPath(POS){
	if(!POS){
		return false;
	}
	searchCount++;
	searchedQ.push(POS);
	if((POS.X == endPos.X && POS.Y == endPos.Y)){
		return true;
	}else{
		var found = false;
		var down = POS.Down();
		var right = POS.Right();
		var up = POS.Up();
		var left = POS.Left();
		
		if(!found && down){
			found = searchPath(down)
		}
		if(!found && right) {
			found = searchPath(right);
		}
		if(!found && up) {
			found = searchPath(up);
		}
		if(!found && left) {
			found = searchPath(left);
		}
		if(!found){
			grid[POS.X][POS.Y] = -1;
			return searchPath(POS.PrePOS);
		}else{
			return true;
		}	
	}
}
function paintSearchResult(closedQ){
	var path = [];
	var lastPOS = closedQ.pop();
	while( lastPOS.X!=startPos.X || lastPOS.Y!=startPos.Y){
		path.push(gridUI[lastPOS.X][lastPOS.Y]);
		lastPOS = lastPOS.PrePOS;
		pathLength++;
	}
	var timer = window.setInterval(function(){
		var point = path.pop();
		if(point) point.style.background="#339933";
		else clearInterval(timer);
	},200);
}

function startSearch(){
	if(searchPath(startPos)){
		paintSearchResult(searchedQ);
		$("msg").innerHTML = "Found a path need: "+ pathLength +" steps. Searched position count: " + searchCount;
	}else{
		$("msg").innerHTML = "Not connected.";
	}
}

</script>
</div>
<div>
</div>
</body>
</html>







  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值