获得有向无环图中起点到终点的所有路径_力扣1514——概率最大的路径
本題主要和圖的遍歷求解最短路徑相關,可以用 Dijkstra 或者 Bellman-Ford 算法進行解決。
原題
給你一個由 n 個節點(下標從 0 開始)組成的無向加權圖,該圖由一個描述邊的列表組成,其中 edges[i] = [a, b] 表示連接節點 a 和 b 的一條無向邊,且該邊遍歷成功的概率為 succProb[i] 。
指定兩個節點分別作為起點 start 和終點 end ,請你找出從起點到終點成功概率最大的路徑,并返回其成功概率。
如果不存在從 start 到 end 的路徑,請 返回 0 。只要答案與標準答案的誤差不超過 1e-5 ,就會被視作正確答案。
示例 1:
輸入:n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.2], start = 0, end = 2輸出:0.25000
解釋:從起點到終點有兩條路徑,其中一條的成功概率為 0.2 ,而另一條為 0.5 * 0.5 = 0.25
示例 2:
輸入:n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.3], start = 0, end = 2輸出:0.30000
示例 3:
輸入:n = 3, edges = [[0,1]], succProb = [0.5], start = 0, end = 2輸出:0.00000
解釋:節點 0 和 節點 2 之間不存在路徑
提示:
2 <= n <= 10^4
0 <= start, end < n
start != end
0 <= a, b < n
a != b
0 <= succProb.length == edges.length <= 2*10^4
0 <= succProb[i] <= 1
每兩個節點之間最多有一條邊
解題
首次嘗試
原本,我想利用樹的深度優先搜索遍歷,加上一定程度的剪枝(就是排除已經遍歷過的節點),完成這道題目,代碼如下:
class Solution {/**
* key為起始點,value為所有相連的點
*/
Map> map;/**
* key為"點A_點B"(A < B),value為對應的概率
*/
Map probMap;double maxProb = -1;int end;public double maxProbability(int n, int[][] edges, double[] succProb, int start, int end) {map = new HashMap<>(n * 4 / 3 + 1);
probMap = new HashMap<>(succProb.length * 4 / 3 + 1);this.end = end;// 構造每個點的相連關系for (int i = 0; i < edges.length; i++) {int[] edge = edges[i];
Setset = map.computeIfAbsent(edge[0], k -> new HashSet<>());set.add(edge[1]);set = map.computeIfAbsent(edge[1], k -> new HashSet<>());set.add(edge[0]);
String key = edge[0] < edge[1] ? (edge[0] + "_" + edge[1]) : (edge[1] + "_" + edge[0]);
probMap.put(key, succProb[i]);
}boolean[] visited = new boolean[n];
dp(start, 1, visited);return maxProb == -1 ? 0 : maxProb;
}public void dp(int index, double prob, boolean[] visited) {// 已到終點if (index == end) {
maxProb = prob > maxProb ? prob : maxProb;return;
}// 獲取當前點可以到達的所有點
Setset = map.get(index);// 如果當前點到達不了其余點if (set == null) {return;
}// 標記當前點已訪問
visited[index] = true;// 遍歷相鄰的點for (int next : set) {if (visited[next]) {continue;
}
String key = index < next ? (index + "_" + next) : (next + "_" + index);// 訪問下一個點
dp(next, prob * probMap.get(key), visited);
}// 退出,將該點標記為未訪問
visited[index] = false;
}
}
但很可惜,超時了。我想了一下,應該是因為沒有借用之前已經計算出來的結果,因此比較浪費時間。
其時間復雜度取決于邊的數量,假設邊的數量是 m ,則時間復雜度為O(m^2)。
而邊 m 與點 n 的關系,m 最小是 0(也就是點之間沒有線),最大是?(n - 1) * n / 2,每個點之間都有連線。
因此可以預見,這樣的算法效率確實很差。
Dijkstra 算法
定義概覽
Dijkstra (迪杰斯特拉)算法是典型的單源最短路徑算法,用于計算一個節點到其他所有節點的最短路徑。主要特點是以起始點為中心向外層層擴展,直到擴展到終點為止。
注意該算法要求圖中不存在負權邊。
算法思想
設 G=(V,E) 是一個帶權有向圖,把圖中頂點集合 V 分成兩組:
第一組為已求出最短路徑的頂點集合(用 S 表示,初始時 S 中只有一個源點,以后每求得一條最短路徑 , 就將加入到集合 S 中,直到全部頂點都加入到 S 中,算法就結束了)。
第二組為其余未確定最短路徑的頂點集合(用 U 表示),按最短路徑長度的遞增次序依次把第二組的頂點加入 S 中。
在加入的過程中,總保持從源點 v 到 S 中各頂點的最短路徑長度不大于從源點 v 到 U 中任何頂點的最短路徑長度。
此外,每個頂點對應一個距離,S 中的頂點的距離就是從 v 到此頂點的最短路徑長度。U 中的頂點的距離,是從 v 到此頂點只包括 S 中的頂點為中間頂點的當前最短路徑長度。
算法步驟
初始時,S 只包含源點,即 S ={v},v 的距離為0。U 包含除 v 外的其他頂點,即: U ={其余頂點},若 v 與 U 中頂點 u 有邊,則
正常有權值,若u不是v的出邊鄰接點,則權值為∞。從U中選取一個距離v最小的頂點k,把k,加入S中(該選定的距離就是v到k的最短路徑長度)。
以k為新考慮的中間點,修改U中各頂點的距離;若從源點v到頂點u的距離(經過頂點k)比原來距離(不經過頂點k)短,則修改頂點u的距離值,修改后的距離值的頂點k的距離加上邊上的權。
重復步驟b和c直到所有頂點都包含在S中。
執行動畫過程如下圖
本題解法
class Solution {public double maxProbability(int n, int[][] edges, double[] succProb, int start, int end) {
// records[i]代表點i相鄰的所有點,以及其概率
List> allRecords = new ArrayList<>(n + 1);for (int i = 0; i < n + 1; i++) {
allRecords.add(new LinkedList<>());
}// 構造每個點的相連關系for (int i = 0; i < edges.length; i++) {int[] edge = edges[i];
List records = allRecords.get(edge[0]);
records.add(new Record(edge[1], succProb[i]));
records = allRecords.get(edge[1]);
records.add(new Record(edge[0], succProb[i]));
}// 利用廣度優先搜索,進行遍歷// 借用優先隊列,保證優先遍歷當前概率高的
PriorityQueuequeue = new PriorityQueue<>();// 記錄從start到每一個點的概率double[] result = new double[n];// 從start開始遍歷queue.offer(new Record(start, 1));
result[start] = 1;// 開始while (!queue.isEmpty()) {// 當前節點
Record record = queue.poll();int node = record.node;double prob = record.prob;// 獲取當前點所能達到的其他節點
List otherNodes = allRecords.get(node);// 遍歷其余節點for (Record next : otherNodes) {int nextNode = next.node;double nextProb = prob * next.prob;// 如果當前計算出的概率,小于等于之前計算的概率if (nextProb <= result[nextNode]) {// 那么就沒有必要繼續算了,直接用之前的即可continue;
}// 更新概率
result[nextNode] = nextProb;// 如果已到結尾或者當前的概率已經比到end的小if (nextNode == end || nextProb < result[end]) {// 那么也沒有必要繼續了continue;
}// 添加節點queue.offer(new Record(nextNode, nextProb));
}
}return result[end];
}class Record implements Comparable<Record> {int node;double prob;public Record(int node, double prob) {this.node = node;this.prob = prob;
}@Overridepublic int compareTo(Record other) {if (other == null) {return -1;
}if (this.prob == other.prob) {return this.node - other.node;
}return this.prob - other.prob > 0 ? -1 : 1;
}
}
}
提交OK,執行用時超過了69%的 java 提交記錄,看來還有值得優化的地方。
假設邊的數量為 m ,點的數量為 n ,則時間復雜度為O(n + m + nlogn)。
Bellman-Ford 算法
之前有說到 Dijkstra 算法要求不能有負權邊,而這個 Bellman-Ford 算法是支持的。
算法步驟
創建源頂點 v 到圖中所有頂點的距離的集合 distSet,為圖中的所有頂點指定一個距離值,初始均為 Infinite,源頂點距離為 0;
計算最短路徑,執行 V - 1 次遍歷;對于圖中的每條邊:如果起點 u 的距離 d 加上邊的權值 w 小于終點 v 的距離 d,則更新終點 v 的距離值 d;
檢測圖中是否有負權邊形成了環,遍歷圖中的所有邊,計算 u 至 v 的距離,如果對于 v 存在更小的距離,則說明存在環;
例如,下面的有向圖 G 中包含 5 個頂點和 8 條邊。假設源點 為 A。初始化 distSet 所有距離為 INFI,源點 A 為 0。
由于圖中有 5 個頂點,按照步驟 1 需要遍歷 4 次,第一次遍歷的結果如下。
第二次遍歷的結果如下。
以此類推可以得出完全遍歷的結果。
本題解法
class Solution {public double maxProbability(int n, int[][] edges, double[] succProb, int start, int end) {
// 記錄結果
double[] result = new double[n];
// 起點
result[start] = 1;
// 從start點出發,先更新直接與start點相連的點的概率,然后逐步更新,直到不需要更新為止
while (true) {
// 是否有過變動
boolean changed = false;
// 遍歷所有邊
for (int j = 0; j < edges.length; j++) {
int[] edge = edges[j];
// 如果從當前點edge[0]出發,到edge[1]的概率,大于之前記錄的結果
if (result[edge[0]] * succProb[j] > result[edge[1]]) {
// 則更新
result[edge[1]] = result[edges[j][0]] * succProb[j];
changed = true;
}
// 因為是無向圖,所以再反向遍歷
if (result[edge[1]] * succProb[j] > result[edge[0]]) {
result[edge[0]] = result[edge[1]] * succProb[j];
changed = true;
}
}
// 一遍未修改則表示圖已遍歷完成
if (!changed) {
break;
}
}
return result[end];
}
}
提交OK,執行用時超過了95%的 java 提交記錄。
其時間假設邊的數量為 m ,點的數量為 n ,則時間復雜度為O(mn)。
總結
以上就是這道題目我的解答過程了,不知道大家是否理解了。本題主要和圖的遍歷求解最短路徑相關,可以用 Dijkstra 或者 Bellman-Ford 算法進行解決。
有興趣的話可以訪問我的博客或者關注我的公眾號、頭條號,說不定會有意外的驚喜。
https://death00.github.io/
公眾號:健程之道
點此評論
總結
以上是生活随笔為你收集整理的获得有向无环图中起点到终点的所有路径_力扣1514——概率最大的路径的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vba 数组赋值_VBA数组与字典解决方
- 下一篇: ih5长图如何滑动_长图怎么一键截取?这