[leetcode/lintcode 題解] Amazon面試題:安排課程
阿新 • • 發佈:2020-10-04
你需要去上n門九章的課才能獲得offer,這些課被標號為 0 到 n-1 。
有一些課程需要“前置課程”,比如如果你要上課程0,你需要先學課程1,我們用一個匹配來表示他們: [0,1]
給你課程的總數量和一些前置課程的需求,返回你為了學完所有課程所安排的學習順序。
可能會有多個正確的順序,你只要返回一種就可以了。如果不可能完成所有課程,返回一個空陣列。
線上評測地址:領釦題庫官網
例1:
例1:
輸入: n = 2, prerequisites = [[1,0]]
輸出: [0,1]
例2:
輸入: n = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
輸出: [0,1,2,3] or [0,2,1,3]
解題思路
對於兩門課之間的約束關係,很容易聯想到圖,我們可以將課抽象為節點,將約束抽象為一條有向邊,可以用有向圖的相關演算法解決問題。拓撲排序正好可以解決這一問題。
演算法:拓撲排序
一個合法的選課序列就是一個拓撲序,拓撲序是指一個滿足有向圖上,不存在一條邊出節點在入節點後的線性序列,如果有向圖中有環,就不存在拓撲序。可以通過拓撲排序演算法來得到拓撲序,以及判斷是否存在環。
拓撲排序步驟:
- 建圖並記錄所有節點的入度。
- 將所有入度為0的節點加入佇列。
- 取出隊首的元素now,將其加入拓撲序列。
- 訪問所有now的鄰接點nxt,將nxt的入度減1,當減到0後,將nxt加入佇列。
- 重複步驟3、4,直到佇列為空。
- 如果拓撲序列個數等於節點數,代表該有向圖無環,且存在拓撲序。
- 建圖,掃描一遍所有的邊O(E)。
- 每個節點最多入隊出隊1次,複雜度O(V)。
- 鄰接表最終會被遍歷1遍,複雜度O(E)。
- 綜上,總複雜度為O(V + E)。
- 鄰接表佔用O(V + E)的空間。
- 佇列最劣情況寫佔用O(V)的空間。
- 綜上,總複雜度為O(V + E)。
public class Solution {
/*
* @param numCourses: a total of n courses
* @param prerequisites: a list of prerequisite pairs
* @return: the course order
*/
public int[] findOrder(int numCourses, int[][] prerequisites) {
List[] graph = new ArrayList[numCourses];
int[] inDegree = new int[numCourses];
for (int i = 0; i < numCourses; i++) {
graph[i] = new ArrayList<Integer>();
}
// 建圖
for (int[] edge: prerequisites) {
graph[edge[1]].add(edge[0]);
inDegree[edge[0]]++;
}
int numChoose = 0;
Queue queue = new LinkedList();
int[] topoOrder = new int[numCourses];
// 將入度為 0 的編號加入佇列
for(int i = 0; i < inDegree.length; i++){
if (inDegree[i] == 0) {
queue.add(i);
}
}
while (! queue.isEmpty()) {
int nowPos = (int)queue.poll();
topoOrder[numChoose] = nowPos;
numChoose++;
// 將每條邊刪去,如果入度降為 0,再加入佇列
for (int i = 0; i < graph[nowPos].size(); i++) {
int nextPos = (int)graph[nowPos].get(i);
inDegree[nextPos]--;
if (inDegree[nextPos] == 0) {
queue.add(nextPos);
}
}
}
if (numChoose == numCourses)
return topoOrder;
return new int[0];
}
}
更多題解參考:九章官網Solution