力扣207、210

前言

207 和 210 本质上一个题,就是改变了输出结果而已,我这两个题一个用于学习,一个用于自我检验,看是否真的搞懂了,建议读者也这么尝试。

题目一

在这里插入图片描述

在这里插入图片描述

力扣:207
力扣:210

思路

图的存储(有向图)

1、根据题意,有一系列的课程以及课程之间的顺序,课程抽象成节点,课程之间的顺序是边,有点集和边集,经典图的问题,所以第一个事情就是图到底该咋存。
2、最常用的方法就是邻接矩阵和邻接表,但是邻接矩阵太废空间,除非图上的边特多以及图不是很大,要不然不用邻接矩阵,我们这里采用邻接表。
3、
在这里插入图片描述

拓扑排序

1、定义:拓扑排序是一个节点序列,保证图中任意一条边(u,v),u在排序序列里一定在v前面
2、通过上方定义可知,有拓扑序列,(前提是在图里面)第一题目中必须给出严格的先后关系,只有有了这种严格的先后关系,才有拓扑排序的必要。第二就是不可能有环路,因为一旦有环路(u,v),(v,u)这样,u必须在v前面,v也必须在u前面,矛盾了,所以没有环路。
3、作用:第一拓扑排序可以在严格要求先后顺序的题目中,给出一个不唯一的、并且符合题目先后顺序要求,的一个结果序列。第二,如果得不到拓扑序列,则说明有环路,可以用来判别环路。
4、本题而言,所有课程就是节点,它规定了严格的课程先后关系,并且答案要求我们输出一个符合要求的任意一个序列,所以这里是用拓扑排序。一旦有环路,输出{}即可,拓扑排序也方便我们判断环路是否存在。

拓扑排序实现

DFS

1、每个节点都规定了3种状态,0:等待、1:正在排序、2:完毕
2、一张大的图,其实可以拆分成若干个树的组合,所以我们从任意一个节点开始走,走完他这棵树之后,再挑一个没走过的节点(状态0)继续走它对应的树
3、每开始处理一个点,节点的状态变成1,等待他的全部子节点都完事了,就剩他自己的时候,那么他的状态就是2,已经排序完毕,把自己入栈。
4、针对任意一个节点u,我们按照深度优先走完它某一个子节点对应的分支,再走他的其他子节点,经典DFS,最后回溯到了自己,这个时候他的全部子节点都已经入栈待好了,按照顺序,这个节点u,应该是他所有子节点的父节点,理应在最上面,此时入栈就在最上面,恰好也满足了拓扑排序的需要。
5、当dfs一个新节点(状态是0),就按照深度优先的规则,开一个新函数走新的节点,,一旦碰到状态为1的节点,说明我没等完事呢走回来了,说明有环路,碰到状态为2的节点,此时那个节点已经在答案中了,说明相对位置不会变了,无影响。

BFS

1、有向图,入边=0,说明我前面根本就没人,我肯定是按照要求排在最前面
2、所以找入边是零的,加入队列,每次从队列头那块去一个节点,加入答案,再把该点对应的边全干掉,重新结算所有点的入度,接着把入度为零的点加入队列,直到队列为空
3、最终结果集点的个数少于目标个数,则有环

代码:(DFS+BFS)

DFS

class Solution {
    
    
public:
	//做图的题之前,一定要事先存储图
	vector<vector<int>> edge;
	//存储每个节点状态
	vector<int> point;
	//答案要vector但是我们确实按照栈来存储记录,所以用数组模拟栈
	vector<int> ans;
	//记录有无环
	bool is_cycle = false;
	
	void dfs(int i) {
    
    
		point[i] = 1;//此时为处理态

		for (int e : edge[i]) {
    
    //加强for,找i的所有边的终点
			if (point[e] == 0) {
    
    //没走过就走一下
				dfs(e);
				if (is_cycle) {
    
    //一旦有换,马上快速退出,快速退出通道
					return;
				}
			}
			else if (point[e] == 1) {
    
    //说明有环
				is_cycle = true;
				return;
			}
		}
		point[i] = 2;//所有儿子节点全完事了,加入栈了,则自己也完事了
		ans.push_back(i);//把自己加入进去,顺序符合拓扑排序
	}

	vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
    
    
		edge.resize(10 * numCourses);
		point.resize(10 * numCourses);//前提开足空间
		for (int i = 0; i < prerequisites.size(); i++) {
    
    //存储图
			vector<int> tmp = prerequisites[i];
			edge[tmp[1]].push_back(tmp[0]);、
			point[i] = 0;//为每一个点标定未访问标签
		}
		for (int i = 0; i < numCourses; i++) {
    
    
			if (is_cycle) {
    
    //发现环路,马上退出
				break;
			}
			if (point[i] == 0) {
    
    
				dfs(i);
			}
		}
		if (is_cycle) {
    
    
			return {
    
    };
		}
		reverse(ans.begin(), ans.end());//因为栈的缘故,所以和答案顺序相反
		return ans;
	}
};


(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短时间):
在这里插入图片描述
在这里插入图片描述

BFS

class Solution {
    
    
public:
	vector<vector<int>> edges;
	vector<int> point;//存每个点入度
	vector<int> ans;
	vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
    
    
		edges.resize(10 * numCourses);
		point.resize(10 * numCourses);
		for (int i = 0; i < prerequisites.size(); i++) {
    
    //存储图
			vector<int> tmp = prerequisites[i];
			edges[tmp[1]].push_back(tmp[0]);//vector表示的邻接表
			point[tmp[0]]++;
		}
		queue<int> tmp;//临时队列
		for (int i = 0; i < numCourses; i++) {
    
    
			if (point[i] == 0) {
    
    
				tmp.push(i);
			}
		}
		while (!tmp.empty()) {
    
    
			int item = tmp.front();
			ans.push_back(item);
			tmp.pop();
			for (int e : edges[item]) {
    
    
				point[e]--;
				if (point[e] == 0) {
    
    
					tmp.push(e);
				}
			}
		}
		if (ans.size() != numCourses) {
    
    
			return {
    
    };
		}
		return ans;
	}
};

(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短时间):
在这里插入图片描述
在这里插入图片描述

Guess you like

Origin blog.csdn.net/qq_45678698/article/details/120471035