深度首先搜索矩阵中的迷宫

时间:2017-09-22 11:51:12

标签: javascript algorithm

我有一个矩阵:

 var matrix = [
        [0, 1, 0, 1, 0],
        [0, 0, 1, 1, 0],
        [0, 0, 1, 1, 0],
        [0, 0, 0, 0, 0],
        [0, 1, 0, 1, 0]
     ];

我需要使用深度优先搜索算法找到从“开始”到“结束”的方式。

var start = [0,4],
    end = [4,0];

我只能向四个方向移动。上,下,左,右。

我试图实现这个算法,但是我的函数找不到解决死锁的方法。该功能找不到其他路径。

也许我误解了算法的运作方式。

您可以在JSFiddle和此处找到我的代码:

let matrix = [
    [0, 1, 0, 1, 0],
    [0, 0, 1, 0, 0],
    [0, 0, 1, 1, 0],
    [0, 0, 0, 0, 0],
    [0, 1, 0, 1, 0]
 ];

let start = [0,4];
let end = [4,0];
let way = [start];

function findWay(position, prevPosition){

	prevPosition = prevPosition || [-1,-1];

	let left  = [ position[0], position[1] - 1 ];
	let right = [ position[0], position[1] + 1 ];
	let up    = [ position[0] - 1, position[1] ];
	let down  = [ position[0] + 1, position[1] ];

	if( position[0] == end[0] && position[1] == end[1] ) {
		return way;
	} 

	if(checkCondition(left, prevPosition)) {
		way.push(left);
		return findWay(left, position);
	}

	if(checkCondition(down, prevPosition)){
		way.push(down);
		return findWay(down, position);
	} 	

	if(checkCondition(right, prevPosition)){
		way.push(right);
		return findWay(right, position);
	}

	if(checkCondition(up, prevPosition)){
		way.push(up);
		return findWay(up, position);
	} 	

    way = 'No Way!';
	return way;
}


function checkCondition(direction, prevPosition){

	if(direction[0] > 4 || direction[0] < 0 || direction[1] > 4 || direction[1] < 0){
		return false;
	}

	if(prevPosition[0] == direction[0] && prevPosition[1] == direction[1]){
		return false;
	}

	if(matrix[direction[0]][direction[1]] == 0){
		return true;
	}

	return false;
}

findWay(start);

请帮我正确编写功能。

2 个答案:

答案 0 :(得分:2)

我发现您的实施存在两个问题:

1)检查访问过的节点。你确实有一张支票,确保你不会回到你刚来的节点,但这还不够。您需要检查是否未返回任何以前访问过的节点。

为此,我会保留一组访问过的节点。每次展开节点时,都将其添加到该阵列。每次您考虑扩展节点时,请检查它是否已存在于已访问的阵列中。所以像这样:

let visited = [];

function findWay(position) {
  visited.push(position);
  // Rest of function omitted.
}

checkCondition(position) {
  // Code for checking out of bounds omitted

  if (visited.find(v => v[0] == position[0] && v[1] == position[1]) {
    return false;
  }

  // Code for checking passable vs unpassable omitted
}

2)扩展节点时,你只能进入最多一个方向;第一个方向恰好是可以通过的。这是因为无论findWay的结果如何,您都会返回。一个简单的解决方法是改变这个:

if(checkCondition(left, prevPosition)) {
  way.push(left);
  return findWay(left, position);
}

...到此

if(checkCondition(left, prevPosition)) {
  way.push(left);
  const result = findWay(left, position);
  if (result !== 'No Way!') {
    return result;
  }
}

......以及其他案例的类似变化。

答案 1 :(得分:0)

您的代码存在一些问题:

  • 明确你要归还的内容。我建议你使用null表示没有找到路径,你可以轻松检查,因为它是一个假值。您只想在找到路径后返回。当你向左走并走向死胡同时,你仍然想要探索其他三个方向。您的代码会立即返回。
  • 你必须检查你是否已经去过一个牢房。仅通过之前访问过的单元格是不够的,因为您可能会走更大的循环。您可以检查当前单元格是否已经在路径中,但通常的做法是通过将所有访问过的单元格设置为1来使其不可用。当然,当您从死角追溯时,必须再次访问这些单元格,所以其他路径可能仍然存在。
  • 同样,你不能只是添加到路上。追溯时,必须再次从路径中删除单元格。否则你会得到一条不一致的道路,甚至包括死路。换句话说,当您回溯时,您必须将程序状态重置为您输入时的状态。
  • 可能更容易检查单元格是否有效,如果不是,则立即返回null

所以这是以下算法的有效实现:

  • 细胞无效吗?如果是,请返回null
  • 是否访问过该单元?如果是,请转发null
  • 细胞是目标吗?如果是,请返回路径。
  • 将单元格标记为已访问并将其添加到路径中。
  • 走进所有四个方向,直到找到有效路径。如果找到,请将其退回。
  • 通过重置单元格和路径进行清理并返回null

在Javascript中:

function findWay(pos, end, way)
{
    if (way === undefined) way = [];

    if (pos[0] < 0 || pos[0] >= matrix.length) return null;
    if (pos[1] < 0 || pos[1] >= matrix[0].length) return null;

    if (pos[0] == end[0] && pos[1] == end[1]) return way;
    if (matrix[pos[0]][pos[1]] != 0) return null;

    matrix[pos[0]][pos[1]] = 1;
    way.push(pos);

    let res = (findWay([pos[0] + 1, pos[1]], end, way)
            || findWay([pos[0], pos[1] + 1], end, way)
            || findWay([pos[0] - 1, pos[1]], end, way)
            || findWay([pos[0], pos[1] - 1], end, way));

    if (res !== null) return res;

    matrix[pos[0]][pos[1]] = 0;
    way.pop();

    return null;
}