【问题标题】:Bus Routes Algorithm公交路线算法
【发布时间】:2019-07-23 07:53:06
【问题描述】:

我正在尝试解决以下问题: https://leetcode.com/problems/bus-routes/

我们有一个巴士路线列表。每条routes[i] 都是一条公交路线,第 ith 路公交车永远重复。例如,如果routes[0] = [1, 5, 7],这意味着第一辆公共汽车(第 0th 索引)以 1→5→7→1→5→7→1→... 的顺序运行。 p>

我们从公共汽车站S 开始(最初不在公共汽车上),我们想去公共汽车站T。仅乘坐公共汽车旅行,我们必须乘坐最少多少辆公共汽车才能到达目的地?如果不可能,则返回 -1。

示例:

输入:

routes = [[1, 2, 7], [3, 6, 7]]
S = 1
T = 6

输出:

2

解释:

最好的策略是乘坐第一辆公交车到 7 号公交车站,然后乘坐第二辆公交车到 6 号公交车站。

注意:

1 <= routes.length <= 500.
1 <= routes[i].length <= 500.
0 <= routes[i][j] < 10 ^ 6.

我的想法是将每个站点视为一个节点。每个节点都有一个颜色(公交车号),并有一个值(站号)。

然后这个问题将被转换为 0-1 BFS 最短路径问题。

这是我的代码:

class Node {
  int val;
  int color;
  boolean visited;
  int distance;

  public Node(int val, int color) {
    this.val = val;
    this.color = color;
    this.visited = false;
    this.distance = 0;
  }

  public String toString() {
    return "{ val = " + this.val  +  ", color = " + this.color + " ,distance = " + this.distance + "}";
  }
}

class Solution {
    public int numBusesToDestination(int[][] routes, int S, int T) {
      if(S == T) return 0;
      // create nodes
      // map each values node(s)
      // distance between nodes of the same bus, have 0 distance
      // if you're switching buses, the distance is 1
      Map<Integer, List<Node>> adjacency = new HashMap<Integer, List<Node>>();
      int color = 0;
      Set<Integer> colorsToStartWith = new HashSet<Integer>();
      for(int[] route : routes) {
        for(int i = 0; i < route.length - 1; i++) {
          int source = route[i];
          int dest = route[i + 1];
          adjacency.putIfAbsent(source, new ArrayList<Node>());
          adjacency.putIfAbsent(dest, new ArrayList<Node>());
          if(source == S) colorsToStartWith.add(color);
          adjacency.get(source).add(new Node(dest, color));
          adjacency.get(source).add(new Node(source, color));
        }
        if(route[route.length - 1] == S) colorsToStartWith.add(color);
        adjacency.putIfAbsent(route[route.length - 1], new ArrayList<Node>());
        adjacency.get(route[route.length - 1]).add(new Node(route[0], color));
        adjacency.get(route[route.length - 1]).add(new Node(route[route.length - 1], color));
        color++;
      }

      // run bfs
      int minDistance = Integer.MAX_VALUE;
      Deque<Node> q = new LinkedList<Node>();
      Node start = new Node(S, 0);
      start.distance = 1;
      q.add(start);
      boolean first = true;
      boolean found = false;
      while(!q.isEmpty()) {
        Node current = q.remove();
        current.visited = true;
        System.out.println(current);
        for(Node neighbor : adjacency.get(current.val)) {
          if(!neighbor.visited) {
            neighbor.visited = true;
            if(neighbor.color == current.color || current.val == neighbor.val || first) {
              q.addFirst(neighbor);
              neighbor.distance = current.distance;
            } else {
              q.addLast(neighbor);
              neighbor.distance = current.distance + 1;
            }
            if(neighbor.val == T) { 
              minDistance = Math.min(minDistance, neighbor.distance);
            }
          }
        }
        first = false;
      }
      return minDistance == Integer.MAX_VALUE ? -1  : minDistance;      
    }
}

我不知道为什么这是错误的。

以下测试用例失败:

Routes = [
    [12,16,33,40,44,47,68,69,77,78,82,86,97],
    [5,8,25,28,45,46,50,52,63,66,80,81,95,97],
    [4,5,6,14,30,31,34,36,37,47,48,55,56,58,73,74,76,80,88,98],
    [58,59],
    [54,56,78,96,98],
    [7,30,35,44,60,87,97],
    [3,5,57,88],
    [3,9,13,15,23,24,28,38,49,51,54,59,63,65,78,81,86,92,95],
    [2,7,16,20,23,46,55,57,93],
    [10,11,15,31,32,48,53,54,57,66,69,75,85,98],
    [24,26,30,32,51,54,58,77,81],
    [7,21,39,40,49,58,84,89],
    [38,50,57],
    [10,57],
    [11,27,28,37,55,56,58,59,81,87,97],
    [0,1,8,17,19,24,25,27,36,37,39,51,68,72,76,82,84,87,89],
    [10,11,14,22,26,30,48,49,62,66,79,80,81,85,89,93,96,98],
    [16,18,24,32,35,37,46,63,66,69,78,80,87,96],
    [3,6,13,14,16,17,29,30,42,46,58,73,77,78,81],
    [15,19,32,37,52,57,58,61,69,71,73,92,93]
]
S = 6
T = 30

我的代码中导致此测试失败的错误是什么?

【问题讨论】:

  • 这是你能想到的最小的测试输入吗?尝试找到一个简单的失败案例并进行调试。

标签: algorithm shortest-path breadth-first-search


【解决方案1】:

您提供的示例输入应返回 1,因为最后一条路线包含源巴士站和目标巴士站(6 和 30):

[3,6,13,14,16,17,29,30,42,46,58,73,77,78,81]

我运行了your code with that input,它返回 1,因此您的解决方案因另一个原因被拒绝,这肯定是超时。

我发现您的代码不是最优的原因有几个:

  • 虽然理想情况下 BFS 应该在找到目标节点后停止,但您的版本必须继续访问 所有 可访问的未访问节点,然后才能决定解决方案是什么。所以即使找到目标和源在同一条路线上,它也会继续切换路线,做很多不必要的工作,因为没有希望找到更短的路径。 p>

    这不是应该的样子。您应该注意以优先考虑不增加距离的边缘的方式执行搜索,并且只有当不再有这些边缘时,才选择将距离增加 1 的边缘。如果您这样做,您可以在找到目标后立即停止搜索。

  • 一个Node 对象被重复创建用于公共汽车站和“颜色”(即路线)的相同组合。因此,当您稍后将visited 设置为true 时,重复的Node 对象仍将具有等于falsevisited,因此将多次访问该公共汽车站而没有收益。

    您应确保仅在尚无具有此类组合的现有对象时创建新的Node 对象。

  • 同一条路线上的两个连续公交车站之间存在边,这意味着您可能需要遍历同一条路线上的多个边,然后才能找到作为目标的边或切换到另一条路线的正确位置。

    整个路线考虑为Node 会更有效:当路线共享至少一个公共汽车站时,它们将被视为已连接(具有边缘)。将路线转换为公交车站的Sets 可以快速轻松地识别这些边缘。

  • 自反边,从和到同一个巴士站,但指定颜色(路线),也不会增加效率。您尝试使用此设置解决的主要问题是确保路线的第一选择是免费的(不被视为交换机)。但是,如果您应用前面的要点,那么这个问题就不再是问题了。

实施

我选择了 JavaScript 作为实现,但我想用 Java 重写它并不难:

function numBusesToDestination (routes, S, T) {
    if (S === T) return 0;
    // Create nodes of the graph
    const nodes = routes;
    // Map bus stops to routes: a map keyed by stops, with each an empty Set as value
    const nodesAtBusStop = new Map([].concat(...routes.map(route => route.map(stop => [stop, new Set]))));
    // ... and populate those empty Sets:
    for (let node of nodes) {
        for (let stop of node) {
            nodesAtBusStop.get(stop).add(node);
        }
    }
    // Build adjacency list of the graph
    const adjList = new Map(nodes.map(node => [node, new Set]));
    for (let [stop, nodes] of nodesAtBusStop.entries()) {
        for (let a of nodes) {
            for (let b of nodes) {
                if (a !== b) adjList.get(a).add(b);
            }
        }
    }
    const startNodes = nodesAtBusStop.get(S);
    const targetNodes = nodesAtBusStop.get(T);
    if (!startNodes || !targetNodes) return -1;
    // BFS
    let queue = [...startNodes];
    let distance = 1;
    let visited = new Set;
    while (queue.length) {
        // Create a new queue for each distance increment
        let nextLevel = [];
        for (let node of queue) {
            if (visited.has(node)) continue;
            visited.add(node);
            if (targetNodes.has(node)) return distance;
            nextLevel.push(...adjList.get(node));
        }
        queue = nextLevel;
        distance++;
    }
    return -1;
};

// I/O handling
(document.oninput = function () {
    let result = "invalid JSON";
    try {
        let routes = JSON.parse(document.querySelector("#inputRoutes").value);
        let S = +document.querySelector("#inputStart").value;
        let T = +document.querySelector("#inputTarget").value;
        result = numBusesToDestination(routes, S, T);
    }
    catch (e) {}
    document.querySelector("#output").textContent = result;
})();
#inputRoutes { width: 100% }
Routes in JSON format:
<textarea id="inputRoutes">[[1,2,7],[3,6,7]]</textarea><br>
Start: <input id="inputStart" value="1"><br>
Target: <input id="inputTarget" value="6"><br>
Distance: <span id="output"></span>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-21
    • 2016-05-26
    相关资源
    最近更新 更多