输入:int[][] routes routes[i]表示第i号公交车的运行线路。如果routes[i]={1,3,5}。说明公交车运行线路是1->3->5。
int S:表示起始站点
int T:表示目的站点
输出:从S到T最少需要几辆公交车。
规则:一开始人没有坐在公交车上,出行方式只用公交车。
分析:很自然的想到以各个站点为节点,站点之间从上一站到下一站作为线。每个站点添加个list维护这个站点属于哪些公交车。
这样的想法很自然,很惯性,确没有什么用。因为要找的是公交车数量的最小值,所以应该重点关注如何从一个公交车跳转到另外一个公交车。直到调到包含T的公交车上。
学习1:官方的solution
把公交车看做是图的节点。要返回最少的公交车数量,就是一个最短路径问题。
如果两个公交车至少有一个站是相同的,那这两个公交车之间有连线。
从起始站开始BFS遍历图,直到遇到目标站。
一个站可能在多个公交车里面有,所以起始队列和终点目标都是多个的。
这里发现BFS总能解决最短路径问题。;例如847,也是最短路径,也用了BFS。
public int numBusesToDestination(int[][] routes, int S, int T) {
//构建图
Map<Integer, List<Integer>> graph = new HashMap<Integer,List<Integer>>();
int N = routes.length;
for(int i=0;i<N;i++){
Arrays.sort(routes[i]);
graph.put(i,new ArrayList<Integer>());
}
for(int i=0;i<N;i++){
for(int j = i+1;j<N;j++){
if(intersection(routes[i],routes[j])){
graph.get(i).add(j);
graph.get(j).add(i);
}
}
}
//遍历
Queue<Point> queue = new ArrayDeque<>();
Set<Integer> seen = new HashSet<>();
List<Integer> targets = new ArrayList<>();
for(int i=0;i<N;i++){
if(Arrays.binarySearch(routes[i],S)!=-1){
queue.offer(new Point(i,0));
seen.add(i);
}
if(Arrays.binarySearch(routes[i],T)!=-1){
targets.add(i);
}
}
while(!queue.isEmpty()){
Point point = queue.poll();
int node = point.x;
int depth = point.y;
if(targets.contains(node)){
return depth+1;
}else{
for(Integer next : graph.get(node)){
if(!seen.contains(next)){
seen.add(next);
queue.offer(new Point(next,depth+1));
}
}
}
}
return -1;
}
/**
* 判断两个数组是否有交集
* @param a
* @param b
* @return
*/
private boolean intersection(int[] a,int[] b) {
int i = 0;
int j = 0;
while(i<a.length && j<b.length){
if(a[i]==b[j]) return true;
if(a[i]<b[j]){
i++;
}else{
j++;
}
}
return false;
}
学习2:如果把公交站点看做图中的节点也是可以的。但是不是以公交车的线路作为连线的。而是一次访问了一辆公交车上所有的站点。理解起来不如上面的解法。
/**
* https://leetcode.com/problems/bus-routes/discuss/122712/Simple-Java-Solution-using-BFS
* 我一定认为要按照公交车的行驶顺利遍历站点。
* 如果roets[0]={1,5,7},如果从5开始,那么1,7可以同时加入队列,因为只要在这辆公交车内不管走多少遍,都是1辆公交车。题目需求求解的也是最少公交车数量,不是站点数量。
* @param routes
* @param S
* @param T
* @return
*/
public int numBusesToDestination(int[][] routes, int S, int T) {
if(S==T) return 0;
Map<Integer,List<Integer>> map = new HashMap<Integer, List<Integer>>();
for(int i=0;i<routes.length;i++){
for(int j=0;j<routes[i].length;j++){
List<Integer> busList = map.getOrDefault(routes[i][j],new ArrayList<>());
busList.add(i);
map.put(routes[i][j],busList);
}
}
Queue<Integer> queue = new ArrayDeque<>();
queue.offer(S);
Set<Integer> seen = new HashSet<>();
int level = 0;
while(!queue.isEmpty()){
int size = queue.size();
level++;
for(int i=0;i<size;i++){
int stop = queue.poll();
if(stop == T) return level;
for(int bus : map.get(stop)){
if(seen.contains(bus)) continue;
seen.add(bus);
for(int j=0;j<routes[bus].length;j++){
queue.offer(routes[bus][j]);
}
}
}
}
return -1;
}