本文针对一个典型的 bfs 问题进行分析。
在处理 leetcode 815.公交路线[https://leetcode-cn.com/problems/bus-routes/] 题目时,使用 bfs 方法进行解答,结果发现超出时间限制。答案中也提供了 bfs 的处理方案,却在时间限制之内,针对这个奇怪的问题,这里进行分析。
仔细读题,可以发现,题目中给了两个量,一个是 站点,另一个为 路线(routes)。(感觉 routes 结构比较类似 邻接表,存储结点之间的连接关系)。路线放置在一个二维数组中,routes[][]
。每条线路 routes[i]
中,放置该线路经过的站点值。数据的规模规定如下:
1 <= routes.length <= 500
1 <= routes[i].length <= 10^5
routes[i] 中的所有值 互不相同
sum(routes[i].length) <= 10^5
0 <= routes[i][j] < 10^6
0 <= source, target < 10^6
在 bfs 的时候,实际上有两个选择,可以以 站点 为目标,进行遍历,也可以以 路线 为目标进行遍历。代码分别如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
|
public int numBusesToDestination1(int[][] routes, int source, int target) { if(source == target) return 0;
Map<Integer, List<Integer>> map = new HashMap<>(); Map<Integer, Integer> passedStops = new HashMap<>(); Deque<Integer> deque = new ArrayDeque<>();
passedStops.put(source, 0); deque.add(source); for(int i = 0; i < routes.length; ++i){ for(int j = 0; j < routes[i].length; ++j){ List<Integer> passedRoutes = map.getOrDefault(routes[i][j], new ArrayList<Integer>()); passedRoutes.add(i); map.put(routes[i][j], passedRoutes); } } if(map.get(source) == null) return -1;
while(!deque.isEmpty()){ int stop = deque.poll(); int currentDis = passedStops.get(stop); List<Integer> passedRoutes = map.get(stop); for(Integer route : passedRoutes){ for(int tmpStop : routes[route]){ if(!passedStops.containsKey(tmpStop)){ if(tmpStop == target) return currentDis + 1; passedStops.put(tmpStop, currentDis + 1); deque.add(tmpStop); } } } } return -1; }
public int numBusesToDestination2(int[][] routes, int source, int target) { if(source == target) return 0;
Map<Integer, List<Integer>> map = new HashMap<>(); Map<Integer, Integer> taken = new HashMap<>(); Deque<Integer> deque = new ArrayDeque<>();
for(int i = 0; i < routes.length; ++i){ for(int j = 0; j < routes[i].length; ++j){ if(routes[i][j] == source){ deque.offer(i); taken.put(i, 1); } List<Integer> passedRoutes = map.getOrDefault(routes[i][j], new ArrayList<Integer>()); passedRoutes.add(i); map.put(routes[i][j], passedRoutes); } }
while(!deque.isEmpty()){ int routeNumber = deque.poll(); int currentRoutesNumbers = taken.get(routeNumber); for(int stop : routes[routeNumber]){ if(stop == target) return currentRoutesNumbers; List<Integer> passedRoutes = map.get(stop); if(passedRoutes == null) continue; for(Integer route : passedRoutes){ if(!taken.containsKey(route)){ taken.put(route, currentRoutesNumbers + 1); deque.add(route); } } } } return -1; }
|
差别分析:设站点的数量为 n, 路线的数量为 m。以站点为对象进行分析的时候, deque 的最大长度应该是 站点的数量,而每一次 while(!deque.isEmpty())
中,是从 站点 -> 经过站点的路线 -> 路线经过的站点进行遍历,复杂度 为 O(m*n),因此总的复杂度为 O(n^2*m)
。而针对路线进行分析,deque 的最大长度为 路线的数量, while(!deque.isEmpty())
中,是从 路线 -> 该路线经过的站点 -> 经过该站点的路径 的过程进行遍历, 因此算法的时间复杂度为 O(n*m^2)
。由于题目中声明,站点的数量规模 sum(routes[i].length) <= 10^5
,而路线的数量规模仅仅为 1 <= routes.length <= 500
,因此,在所有数据规模都为上限的情况下,以路线为对象进行 bfs,复杂度要远远低于以 站点为对象进行 bfs。