210課程表 II(拓撲排序廣度優先搜索、深度優先搜索——困難)

1、題目描述

現在你總共有 n 門課需要選,記爲 0 到 n-1。

在選修某些課程之前需要一些先修課程。 例如,想要學習課程 0 ,你需要先完成課程 1 ,我們用一個匹配來表示他們: [0,1]

給定課程總量以及它們的先決條件,返回你爲了學完所有課程所安排的學習順序。

可能會有多個正確的順序,你只要返回一種就可以了。如果不可能完成所有課程,返回一個空數組。

說明:

  • 輸入的先決條件是由邊緣列表表示的圖形,而不是鄰接矩陣。詳情請參見圖的表示法。
  • 你可以假定輸入的先決條件中沒有重複的邊。

2、示例

輸入: 2, [[1,0]] 
輸出: [0,1]
解釋: 總共有 2 門課程。要學習課程 1,你需要先完成課程 0。因此,正確的課程順序爲 [0,1] 。
示例 2:輸入: 4, [[1,0],[2,0],[3,1],[3,2]]
輸出: [0,1,2,3] or [0,2,1,3]
解釋: 總共有 4 門課程。要學習課程 3,你應該先完成課程 1 和課程 2。並且課程 1 和課程 2 都應該排在課程 0 之後。因此,一個正確的課程順序是 [0,1,2,3] 。另一個正確的排序是 [0,2,1,3] 。

3、題解
解法一:

基本思想:拓撲排序,給定一個包含n個節點的有向圖G,給出它的節點編號的一種排列,如果滿足:對於圖G中的任意一條有向邊(u,v),u在排列中都出現在v的前面,稱該排列是圖G的拓撲排序。

基本思想:廣度優先搜索

  • 藉助一個隊列queue,將所有入度爲0的節點入隊
  • 當queue非空時,依次將隊首節點cur出隊同時加入res,執行numCourses--,並將cur的所有後繼節點的入度 - 1,如果後繼節點的入度爲0則也入隊。
  • 若整個課程安排圖是有向無環圖,則所有節點一定都入隊並出隊過,最後numCourses = 0,即完成拓撲排序返回res。
  • 若課程安排圖中存在環路,一定有節點的入度始終不爲0,最後numCourses > 0,即不能完成拓撲排序返回空。

解法二:

基本思想:深度優先搜索,判斷圖中是否有環的同時拓撲排序保存至res。
計算圖中節點的入度,從入度爲0的節點開始深度優先搜索,搜索到最後一個節點依次遞歸調用節點返回保存至res
藉助一個標誌列表flags,用於判斷每個節點i的狀態:

  • 未被 DFS 訪問:i=0;
  • 已被其他節點啓動的 DFS 訪問:i=-1;
  • 已被當前節點啓動的 DFS 訪問:i=1。
#include<iostream>
#include<vector>
#include<algorithm>
#include<deque>
#include<map>
using namespace std;
class Solution {
public:
	vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
		//基本思想:拓撲排序,給定一個包含n個節點的有向圖G,給出它的節點編號的一種排列,如果滿足:
		//對於圖G中的任意一條有向邊(u,v),u在排列中都出現在v的前面,稱該排列是圖G的拓撲排序。
		//基本思想:廣度優先搜索
		//藉助一個隊列queue,將所有入度爲0的節點入隊
		//當queue非空時,依次將隊首節點cur出隊同時加入res,執行numCourses--,並將cur的所有後繼節點的入度 - 1,如果後繼節點的入度爲0則也入隊。
		//若整個課程安排圖是有向無環圖,則所有節點一定都入隊並出隊過,最後numCourses = 0,即完成拓撲排序返回res。
		//若課程安排圖中存在環路,一定有節點的入度始終不爲0,最後numCourses > 0,即不能完成拓撲排序返回空。
		vector<int> res;
		deque<int> queue;
		multimap<int, int> HashMap;
		vector<int> inDegree(numCourses, 0);
		int cur;
		for (int i = 0; i < prerequisites.size(); i++)
		{
			inDegree[prerequisites[i][1]]++;
			HashMap.insert({ prerequisites[i][0] ,prerequisites[i][1] });
		}
		for (int i = 0; i < numCourses; i++)
		{
			if (inDegree[i] == 0)
				queue.push_front(i);
		}
		while (!queue.empty())
		{
			cur = queue.back();
			res.push_back(cur);
			queue.pop_back();
			numCourses--;
			int cnt = HashMap.count(cur);
			auto iter = HashMap.find(cur);
			while (cnt--)
			{
				inDegree[iter->second]--;
				if (inDegree[iter->second] == 0)
					queue.push_front(iter->second);
				iter++;
			}
		}
		reverse(res.begin(), res.end());
		return (numCourses == 0) ? res : vector<int>{};
	}
};
class Solution1 {
public:
	multimap<int, int> HashMap;    //保存圖中節點關係
	vector<int> res;    //最後返回的拓撲排序
	vector<int> inDegree;    //計算節點的入度
	vector<int> flags;    //標記每個節點i的狀態
	vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
		//基本思想:深度優先搜索,判斷圖中是否有環的同時拓撲排序保存至res。
		//計算圖中節點的入度,從入度爲0的節點開始深度優先搜索,搜索到最後一個節點依次遞歸調用節點返回保存至res
		//藉助一個標誌列表flags,用於判斷每個節點i的狀態:
		//未被 DFS 訪問:i=0;
		//已被其他節點啓動的 DFS 訪問:i=-1;
		//已被當前節點啓動的 DFS 訪問:i=1。
		for (int i = 0; i < numCourses; i++)
		{
			inDegree.push_back(0);
			flags.push_back(0);
		}
		//計算所有節點的入度,並建立HashMap保存節點關係方便快速查找當前節點的後繼節點
		for (int i = 0; i < prerequisites.size(); i++)
		{
			inDegree[prerequisites[i][1]]++;
			HashMap.insert({ prerequisites[i][0],prerequisites[i][1] });
		}
		for (int i = 0; i < numCourses; i++)
		{
			if (inDegree[i] == 0)
			{
				if (dfs(i) == false)
					return {};
			}
		}
		return (res.size() == numCourses) ? res : vector<int>();
	}
	bool dfs(int cur)
	{
		if (flags[cur] == -1)
			return true;
		if (flags[cur] == 1)
			return false;
		flags[cur] = 1;
		int cnt = HashMap.count(cur);
		auto iter = HashMap.find(cur);
		while (cnt--)
		{
			if (dfs(iter->second) == false)
				return false;
			iter++;
		}
		res.push_back(cur);
		flags[cur] = -1;
		return true;
	}
};
int main()
{
	Solution1 solute;
	int numCourses = 3;
	vector<vector<int>> prerequisites = { {1,0},{2,0 } };
	vector<int> res = solute.findOrder(numCourses, prerequisites);
	for_each(res.begin(), res.end(), [](const auto v) {cout << v << endl; });
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章