因此就出現了對Hackerrank編程大賽這一挑戰被稱爲「無環圖」,這基本上可以歸結爲一個「向非循環圖計算節點的每個節點可到達的數」。例如,假設你有一個圖表,像這樣:可達性計數在向非循環圖的所有節點
[ 1 ] ---->[ 2 ]--->[ 4 ]--->[ 5 ]
[ 3 ] ------/
可達數(包括原始節點):
Node 1: 4
Node 2: 3
Node 3: 4
Node 4: 2
Node 5: 1
我的做法是一個「深度優先」的穿越與記憶化。環顧四周相當多,但它好像在運行時無法得到更進一步,因爲發生的情況下,像這樣的過度計數的改進:
[ 1 ] ---->[ 2 ]--->[ 4 ]--->[ 5 ]
[ 3 ] ------/--------/
第三個節點將計算第四節點,甚至儘管第二個節點已經計入第四個節點。爲了讓事情變得更糟,我只用JavaScript解決了這些挑戰。它是我的主要語言,我從推動其界限中獲得快感。領導委員會中沒有人用JavaScript解決它,但我認爲這是可能的。比賽結束後,我設法通過13的24個測試案例用下面的代碼:
function Solution(graph, nodes) {
var memory = new Array(nodes + 1)
, result = 0;
graph.forEach((a, v) => DepthFirstSearch(graph, v, memory));
// challenge asks for an output variation, but the accurate
// reachability count of every node will be contained in "d.length".
memory.forEach((d, i) => { if (i && (2 * d.length) >= nodes) result++; });
return result;
}
function DepthFirstSearch(graph, v, memory) {
if (memory[ v ]) return memory[ v ];
var descendants = new Uint16Array([ v ]);
graph[ v ].forEach(u => {
descendants = MergeTypedArrays(
DepthFirstSearch(graph, u, memory),
descendants
);
});
// make elements unique
// to avoid over counting
return memory[ v ] = Uint16Array.from(new Set(descendants));
}
function MergeTypedArrays(a, b) {
var c = new a.constructor(a.length + b.length);
c.set(a);
c.set(b, a.length);
return c;
}
// adjacency list
var graph = [
[], // 0
[ 2 ], // 1
[ 4 ], // 2
[ 2 ], // 3
[ 5 ], // 4
[] // 5
];
var nodes = 5;
Solution(graph, nodes);
它未能超過50KB更大的所有投入,以大組節點和邊緣(即5萬個節點和大概投入40,000條邊)。無法識別或設想更快,更高效的內存算法,我完全喪失了接下來要嘗試的內容。思考如何使DFS迭代,但我認爲記憶數千個數組的內存消耗會使這個問題變得更加嚴重,這似乎是主要問題。我在Hackerrank上獲得了11次失敗的測試(與「超時」相反)的「Abort Called」和「Runtime Error」。還嘗試過使用「union」的「bitSets」,但由於bitSets數組需要足夠大以存儲高達50,000的數字,所以內存消耗變得更糟。
約束:
1 ≤ n,m ≤ 5×10^4
1 ≤ a(i),b(i) ≤ n and a(i) ≠ b(i)
It is guaranteed that graph G does not contain cycles.
只想說清楚,我不會得到任何分通過了所有的測試,因爲這個挑戰被鎖定,這是教育的目的,主要是優化。我意識到相關的SO帖子指向拓撲排序,但據我所知,拓撲排序仍然會超過上述情況,因此不是一個可行的解決方案。如果我誤解了,請賜教。預先感謝您的時間。
問:如何優化這進一步?有沒有更高效的方法?
您是否有更多關於測試用例的信息,例如他們有什麼時間/記憶限制?說實話,50k節點的40k邊是一個小圖,即使是一個糟糕的實現也不應該有問題。 – MartinS
@MartinS是的,JavaScript最大執行時間是10秒,最大內存是512MB。我只是在本地運行你的版本;堆棧溢出的迭代錯誤和迭代錯誤需要大約13分鐘[http://marceli.no/r-files/hackerrank-acyclic-graph/input11.txt]測試用例(第一個數字是節點的數量,它旁邊的數字是邊緣的數量,這兩個數字下面的線條是實際的邊緣)。挑戰成功率爲7.87%。我認爲它比我們意識到的還多一點。我一直在回憶記憶,因爲它正在做很多重複的工作。 –
這些約束中的'a和b'是什麼?1≤n,m≤5×10 ^4'1≤a(i),b(i)≤n且a(i)≠b(i) ? – MartinS