【问题标题】:How to stop variable over writing when iterating through object遍历对象时如何停止变量覆盖
【发布时间】:2020-11-12 14:15:59
【问题描述】:

所以我正在尝试实现一个简单的 BFS 搜索来解决迷宫问题。我正在使用的代码顶部有一个 test_graph。当我遍历这段代码时:

        for (var neighbour in graph[vertex])
        {
            console.log("neighbour :", neighbour);
            console.log("path :", path)
            
            let new_path = path;
            new_path.push(neighbour);

            console.log("new path :", new_path);
        }

我得到了输出:

neighbour : A
bfs.js:38 path : ["start"]
bfs.js:43 new path : (2) ["start", "A"]
bfs.js:37 neighbour : B
bfs.js:38 path : (2) ["start", "A"]
bfs.js:43 new path : (3) ["start", "A", "B"])

但是,我希望 new_path 的第二次迭代是 ["start", "B"] 而不是 ["start", "A", "B"]。我想将邻居(A和B)每个附加到“start”以创建数组[“start”,“A”]和[“start”,“B”]并将其推入队列。

这里是完整的代码:

const test_graph = {
    start: {A: 1, B: 1},
    A: {C: 1, D: 1},
    B: {A: 1, D: 1},
    C: {D: 1, finish: 1},
    D: {finish: 1},
    finish: {}
};

// Breadth-first search
const BFS = (graph, start, fin) => {
    var queue = [];
    queue.push([start]);
    var visited = [];

    while (Array.isArray(queue) && queue.length)
    {
        // get first path on queue
        var path;
        path = queue.shift();
        //console.log(path); // ["start"]
        
        // get the last node in the path
        var vertex = path[path.length - 1];
        //console.log(vertex); // start
        
        // if end
        if (vertex == fin)
        {
            return path;
        }
        else if (!visited.includes(vertex))
        {
            // for all adjvant nodes, construct new path and push into queue
            for (var neighbour in graph[vertex])
            {
                console.log("neighbour :", neighbour);
                console.log("path :", path)
                
                let new_path = path;
                new_path.push(neighbour);
                queue.push(new_path);    
                console.log("new path :", new_path);
            }
        }
        return;
    }
}

(return 就是为了解决这个问题)

【问题讨论】:

    标签: javascript arrays loops object breadth-first-search


    【解决方案1】:

    let new_path = path; 创建path 的别名,推送到它(与直接推送到path 相同),然后在打印后丢弃块末尾的别名。这意味着您一直在使用相同的 path 数组。

    很可能,您希望queue.push(path.concat(neighbour)) 分配一个新数组,连接新的neighbour 顶点并将其推送到队列中,有效地将当前路径扩展一个节点,但无需像push 那样修改它.

    除此之外,所有值为1 的对象似乎有点像邻接列表的反模式。 Set 或数组在这里看起来更直观,所以我冒昧地进行了调整以及其他一些调整。

    visited 是一个数组意味着查找的复杂度是 O(n)。使用 Set 可以为您提供 O(1) 查找,并且是此类成员资格测试的正确结构。事实上,visited 在原始版本中是一个未使用的变量,所以如果图有一个循环,我们就会得到一个无限循环。

    Array.isArray(queue) 是不必要的检查--queue 纯粹是本地的,如果我们将其设置为const,我们将无法重新分配它。

    最后,当vertex 不在图中时,通过将 null 合并到一个空数组以避免崩溃来处理它不是一个坏主意(您可能希望输入总是格式正确,但这是一个非常容易的调整)。

    代码如下:

    const shortestPathBFS = (graph, start, destination) => {
      const queue = [[start]];
      const visited = new Set();
    
      while (queue.length) {
        const path = queue.shift();
        const vertex = path[path.length-1];
    
        if (vertex === destination) {
          return path;
        } 
        else if (!visited.has(vertex)) {
          visited.add(vertex);
    
          for (const neighbour of (graph[vertex] || [])) {
            queue.push(path.concat(neighbour));
          }
        }
      }
    }
    
    const G = {
      start: [..."AB"],
      A: [..."CD"],
      B: [..."AD"],
      C: ["D", "finish"],
      D: ["finish"],
      finish: [],
    };
    console.log(shortestPathBFS(G, "start", "finish"));

    【讨论】:

    • 太棒了,谢谢。非常感谢您的深入见解,并已将其采纳!
    猜你喜欢
    • 2014-02-27
    • 2016-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-25
    • 2013-05-05
    • 2021-08-20
    • 2017-11-12
    相关资源
    最近更新 更多