1. 程式人生 > >LeetCode 207. Course Schedule

LeetCode 207. Course Schedule

文章目錄


原題地址

題目要求

There are a total of n courses you have to take, labeled from 0 to n-1.

Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]

Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?

Example 1:

Input: 2, [[1,0]] 
Output: true
Explanation: There are a total of 2 courses to take. 
             To take course 1 you should have finished course 0. So it is possible.

Example 2:

Input: 2, [[1,0],[0,1]]
Output: false
Explanation: There are a total of 2 courses to take. 
             To take course 1 you should have finished course 0, and to take course 0 you should
             also have finished course 1. So it is impossible.

Note:

  1. The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
  2. You may assume that there are no duplicate edges in the input prerequisites.

題目解釋:

輸入包括一個整數代表圖的大小,還有一個vector儲存有向圖的邊,要求如果一個節點A指向另一個節點B,那麼節點B就不能出現在節點A的前面。

這是一個很經典的拓撲排序問題。

拓撲排序

首先,先介紹一下拓撲排序的含義;

拓撲排序是圖論中一個很經典的演算法;對於一個有向無環圖G進行拓撲排序,就是將G中的所有節點排成一個線性序列;對於圖G中的每一條邊,如邊(u,v),則表示在我們輸出的線性序列中節點u必須出現在節點v的前面,這個線性序列就被我們稱為滿足拓撲排序序列,簡稱拓撲序列。

需要注意的是,拓撲排序是針對有向無環圖的;

拓撲排序的實現也並不複雜:

(1)首先,我們需要選取圖中一個入度為0的節點(入度表示某點作為圖中邊的終點的次數之和)

(2)然後從圖中刪除以該節點為起點的所有有向邊和該節點,此時被刪除的有向邊的終點的入度減 1;

(3)重複上面的(1)和(2)操作直到圖中沒有入度為0的節點;

當演算法結束後,如果圖中仍然還有節點,那麼說明該有向圖不存在拓撲序列;反之,存在拓撲序列;

下圖是拓撲排序的一個簡單例子:

由此可以得到拓撲序列{1,2,3,4}

我們還可以看到,在第二步移除節點2和移除節點3的效果是一樣的,因此拓撲排序並不是唯一的。

講完了拓撲排序的原理,下面就是程式碼實現

程式碼實現

程式碼解釋已經註釋了,應該不難理解:

/************************Graph類************************/
class Graph{
private:
	int nums;                 // 圖的頂點個數 
	std::vector<int>* adj;    // 鄰接表儲存有向邊 
	vector<int> indegree;     // 記錄每個節點的入度 
public:
	/*
	 * 建構函式
	 * 輸入:圖的個數  
	 */ 
	Graph(int v){               
		nums = v;
		// 初始化鄰接表和每個節點的入度 
		adj = new std::vector<int>[nums];
		for (int i = 0; i < v; ++i)
		{
			indegree.push_back(0);
		}
	}

	/* 解構函式 */
	~Graph(){
		delete []adj;
	}

	/*
	 * 函式說明:新增有向邊
	 * 輸入:
	 *       from: 起始點 
	 *       to:   終點   
	 */ 
	void addEdge(int from,int to){
		// 往鄰接表中新增有向邊 
		adj[from].push_back(to);
		// 修改終點的入度 
		indegree[to]++;
	}
	
	/*
	 * 函式說明:判斷是否存在拓撲序列 
	 * 輸出:布林值,true表示存在拓撲序列 
	 */ 
	bool topological_sort(){
		// 使用一個佇列儲存入度為0的節點 
		queue<int> q;
		
		for(int i = 0;i < nums;i++){
			if(indegree[i] == 0){
				q.push(i);
			}
		}
		
		// count記錄已經從圖中刪除的節點個數,當count等於節點個數,說明存在拓撲序列 
		int count = 0,temp;
		
		while(!q.empty()){ // 跳出迴圈條件:圖中已經不存在入度為0的節點 
			temp = q.front();
			q.pop();
			count++;
			
			// 更新入度和儲存入度為0的節點的佇列 
			for(auto value:adj[temp]){
				indegree[value]--;
				if(indegree[value] == 0){
					q.push(value);
				}
			}
		}
		return (count == nums);
	}
};

/************************Solution類************************/
class Solution {
public:
    bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
    	// 初始化圖 
        Graph graph(numCourses);
        for(auto value:prerequisites){
			graph.addEdge(value.first,value.second);
		}
		// 拓撲排序 
		return graph.topological_sort();
    }
};