广度优先算法 (BFS) js Demo

BFS算法原理:从起始点开始,分别考察距离起始点为1,2,3...的点,直到找到结束点,或者考察完整个可达区域依然没有找到结束点。

实现方法:首先将起点加入队列,判断当前点是否为终点,若否,则取起点的四个方向的点加入队列(一般四方位考察顺序为下右上左),依次类推,直到队列为空或找到终点。

效果如下,如有发现错误欢迎批评指正:

起点和终点:


点击设置障碍:


搜索路径:



算法核心代码:

function Position(x,y,lastPOS){
	this.X=x;
	this.Y=y;
	this.LastPOS = lastPOS;
}
Position.prototype.validate = function(currPos,queue,closedQ){
	//1 在方格范围之内的,2 非障碍物,3 不在open列表中,4 不在closed列表中
	if(currPos.X >=0 && currPos.X < X && currPos.Y >=0 && currPos.Y < Y && grid[currPos.X][currPos.Y]==1 && !queue.has(currPos) && !closedQ.has(currPos)){
		return true;
	}
	return false;
}
Position.prototype.Down = function(queue,closedQ){
	var curr = new Position(this.X + 1, this.Y);
	if(this.validate(curr,queue,closedQ)){
		curr.LastPOS = this;
		return curr;
	}
	return undefined;
};
Position.prototype.Right = function(queue,closedQ){
	var curr = new Position(this.X, this.Y + 1);
	if(this.validate(curr,queue,closedQ)){
		curr.LastPOS = this;
		return curr;
	}
	return undefined;
};
Position.prototype.Up = function(queue,closedQ){
	var curr = new Position(this.X - 1, this.Y);
	if(this.validate(curr,queue,closedQ)){
		curr.LastPOS = this;
		return curr;
	}
	return undefined;
};
Position.prototype.Left = function(queue,closedQ){
	var curr =  new Position(this.X, this.Y - 1);
	if(this.validate(curr,queue,closedQ)){
		curr.LastPOS = this;
		return curr;
	}
	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 true;
			}
		}
		return false;
	}
	this.Item = _list;
}

function searchPath(){
	var openQ = new Queue(),found = false;
	var closedQ = new Queue();
	var searchCount = 0;
	openQ.push(startPos);
	while(!found && openQ.length() && searchCount < 1000){
		searchCount++;
		var POS = openQ.fetch();
		closedQ.push(POS);
		if(POS.X == endPos.X && POS.Y == endPos.Y){
			found = true;
		}else{
			var down = POS.Down(openQ,closedQ);
			var right = POS.Right(openQ,closedQ);
			var up = POS.Up(openQ,closedQ);
			var left = POS.Left(openQ,closedQ);
			
			if(down) openQ.push(down);
			if(right) openQ.push(right);
			if(up) openQ.push(up);
			if(left) openQ.push(left);
		}
	}
	
	if(found){
		//to do
	}else{
		//to do
	}
}

附上html源代码:

<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="searchPath();" />
<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 startPos = new Position(0,0), endPos = new Position(0,0);

initGrid();
function $(id) {
	return document.getElementById(id);
}
function initGrid(){
	pathLength = 0;
	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(currPos,queue,closedQ){
	//1 在方格范围之内的,2 非障碍物,3 不在open列表中,4 不在closed列表中
	if(currPos.X >=0 && currPos.X < X && currPos.Y >=0 && currPos.Y < Y && grid[currPos.X][currPos.Y]==1 && !queue.has(currPos) && !closedQ.has(currPos)){
		return true;
	}
	return false;
}
Position.prototype.Down = function(queue,closedQ){
	var curr = new Position(this.X + 1, this.Y);
	if(this.validate(curr,queue,closedQ)){
		curr.PrePOS = this;
		return curr;
	}
	return undefined;
};
Position.prototype.Right = function(queue,closedQ){
	var curr = new Position(this.X, this.Y + 1);
	if(this.validate(curr,queue,closedQ)){
		curr.PrePOS = this;
		return curr;
	}
	return undefined;
};
Position.prototype.Up = function(queue,closedQ){
	var curr = new Position(this.X - 1, this.Y);
	if(this.validate(curr,queue,closedQ)){
		curr.PrePOS = this;
		return curr;
	}
	return undefined;
};
Position.prototype.Left = function(queue,closedQ){
	var curr =  new Position(this.X, this.Y - 1);
	if(this.validate(curr,queue,closedQ)){
		curr.PrePOS = this;
		return curr;
	}
	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 true;
			}
		}
		return false;
	}
	this.Item = _list;
}

function searchPath(){
	var openQ = new Queue(),found = false;
	var closedQ = new Queue();
	var searchCount = 0;//If search more than 10000 times, end. 
	openQ.push(startPos);
	while(!found && openQ.length() && searchCount < 10000){
		searchCount++;
		var POS = openQ.fetch();
		closedQ.push(POS);
		if(POS.X == endPos.X && POS.Y == endPos.Y){
			found = true;
		}else{
			var down = POS.Down(openQ,closedQ);
			var right = POS.Right(openQ,closedQ);
			var up = POS.Up(openQ,closedQ);
			var left = POS.Left(openQ,closedQ);
			
			if(down) openQ.push(down);
			if(right) openQ.push(right);
			if(up) openQ.push(up);
			if(left) openQ.push(left);
		}
	}
	
	if(found){
		paintSearchResult(closedQ);
		$("msg").innerHTML = "At least "+ pathLength +" steps. Searched position count: " + searchCount;
	}else{
		$("msg").innerHTML = "Not connected.";
	}
}


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);
}



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



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值