LeetCode:訪問所有節點的最短路徑【847】
題目描述
給出 graph
為有 N 個節點(編號為 0, 1, 2, ..., N-1
)的無向連通圖。
graph.length = N
,且只有節點 i
和 j
連通時,j != i
在列表 graph[i]
中恰好出現一次。
返回能夠訪問所有節點的最短路徑的長度。你可以在任一節點開始和停止,也可以多次重訪節點,並且可以重用邊。
示例 1:
輸入:[[1,2,3],[0],[0],[0]] 輸出:4 解釋:一個可能的路徑為 [1,0,2,0,3]
示例 2:
輸入:[[1],[0,2,4],[1,3,4],[2],[1,2]] 輸出:4 解釋:一個可能的路徑為 [0,1,4,2,3]
提示:
1 <= graph.length <= 12
0 <= graph[i].length < graph.length
題目分析
1.拿到這個問題時,我們先進行初步分析:
分析示例,我們可以知道每個節點是可以被重復訪問的,這樣子就出現了一個問題,很有可能程序將陷入一個死循環,不斷在某幾個節點直接無線循環下去。
為了解決這個問題,我們可以使用二進制值來保存節點的訪問狀態:
2.該用什么算法來描述訪問所有節點的問題呢?
訪問所有節點其實就是一種搜索算法,只是現在我們不是搜索每個確定的節點值,而是有條件的狀態搜索(比如目標狀態為1111)。
題目中說,你可以在任一節點開始和停止,那每個節點都應該是搜索的一種初始狀態,並且從每個節點的這個初始狀態去探索其他狀態,並且最終找到目標狀態前,遍歷了所有的可能性,是一種典型的BFS算法應用。
3.BFS算法的過程是怎么樣的?
我們都知道BFS搜索的算法描述是一棵樹,那么算法的第一層是每個節點,接下來每層數的擴展都算路徑中的一步(因為每一層代表了所有的可能性,在這么多可能性中至少有一種是可以最終找到目標狀態的),最后知道找到目標狀態,返回的步驟就是最短路徑。
4.二進制狀態與搜索的互相作用是怎么樣的?
我們說了每一次搜索都要參考二進制狀態,來避免死循環。每個節點都要一張所有狀態的表格,比如第一個節點的某個狀態被激活了,后來在某一次搜索中又回到第一個節點,並且狀態發現這個狀態出現過,那么很顯然,這樣走下去就會死循環,我們就可以跳過這種情況。
這樣會出現一個情況,同一種狀態,對於不同節點來說是不一樣的,對於1號節點可能是死循環,但是對與2號節點來說很可能是正常的。
5.節點與狀態之間的轉換是怎樣的?就是從一個節點到另一個節點,狀態是怎么變化的?
這里我們采用了或的方式來進行狀態轉換控制,比如訪問0號節點的時候狀態是0001,接下來要訪問2號節點,那么狀態就會變為(0001 | 0100 =0101)!
Java實現
package graph; import java.util.LinkedList; import java.util.Queue; public class ShortestPathLength_847 { public int shortestPathLength(int[][] graph) { int kAns = (1<<graph.length)-1; Queue<Pair> q = new LinkedList<>(); int[][] visited = new int[graph.length][1<<graph.length]; for(int i=0;i<graph.length;i++) q.offer(new Pair(i,1<<i)); int steps =0; while (!q.isEmpty()) { int s = q.size(); while (s-->0) { Pair pair = q.poll(); int n = pair.i; int state = pair.j; if(state==kAns) return steps; if(visited[n][state]==1) continue; visited[n][state]=1; for(int next:graph[n]) q.offer(new Pair(next,state|(1<<next))); } steps++; } return -1; } class Pair{ int i,j; public Pair(int i, int j) { this.i = i; this.j = j; } }