Leetcode算法刷题笔记6-图

Leetcode算法刷题笔记6-图

  • Leetcode 207. 课程表
    • 方法壹 拓扑排序
    • 方法贰 DFS
  • 判断一个无向图是否有环
  • 判断一个有向图是否有环

相关刷题笔记博客
竞赛常用模板整理(ACM/ICPC/CCSP)
常见图论优化

Leetcode算法刷题笔记1-链表
Leetcode算法刷题笔记2-栈、队、堆
Leetcode算法刷题笔记3-递归与回溯
Leetcode算法刷题笔记4-贪心
Leetcode算法刷题笔记5-二叉树
Leetcode算法刷题笔记6-图
Leetcode算法刷题笔记7-动态规划
Leetcode算法刷题笔记8-二分查找

Leetcode 207. 课程表

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/course-schedule/

你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1。

在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]

给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?

示例 1:

输入: 2, [[1,0]] 输出: true 解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。

示例 2:

输入: 2, [[1,0],[0,1]] 输出: false 解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成​课程
0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。

提示:

输入的先决条件是由 边缘列表 表示的图形,而不是邻接矩阵 。
详情请参见图的表示法。
你可以假定输入的先决条件中没有重复的边。
1 <= numCourses <= 10^5

方法壹 拓扑排序

#include
using namespace std;
struct GraphNode{
	int label;
	vector<GraphNode *> neighbors;
	GraphNode(int x) :label(x){};
}; 
//Leetcode提交部分 
class Solution {
public:
    bool canFinish(int numCourses, vector<pair<int,int> > & prerequisites) {
		vector<GraphNode*>graph;
		vector<int> degree;
		for(int i=0;i<numCourses;i++){
			degree.push_back(0);
			graph.push_back(new GraphNode(i));
		} 
		for(int i=0;i<prerequisites.size();i++){
			GraphNode *end   = graph[prerequisites[i].first];
			GraphNode *begin = graph[prerequisites[i].second];	
			begin->neighbors.push_back(end); 
			degree[prerequisites[i].first]++;
		}
		queue<GraphNode*> Q;
		for(int i=0;i<numCourses;i++){
			if(degree[i]==0){
				Q.push(graph[i]);
			}
		}
		while(!Q.empty()){
			GraphNode* node = Q.front();
			Q.pop();
			for(int i=0;i<node->neighbors.size();i++){
				degree[node->neighbors[i]->label]--;
				if(degree[node->neighbors[i]->label]==0){
					Q.push(node->neighbors[i]);
				}
			}
		}
		for(int i=0;i<graph.size();i++){
			delete graph[i];
		}
		for(int i=0;i<degree.size();i++){
			if(degree[i]){
			 	return false;
			}
    	}
    	return true; 
    }
};
//Leetcode自行使用编译器(如DEV\VC\VS)测试部分 
int main(){
	vector<pair<int,int> >prerequisites;
	prerequisites.push_back(make_pair(1,0));
	prerequisites.push_back(make_pair(2,0));
	prerequisites.push_back(make_pair(3,1));
	prerequisites.push_back(make_pair(3,2));
	Solution solve;
	cout<<solve.canFinish(4,prerequisites)<<endl;
	return 0;
}

方法贰 DFS

#include
using namespace std;
struct GraphNode{
	int label;
	vector<GraphNode *> neighbors;
	GraphNode(int x) :label(x){};
}; 
//Leetcode提交部分 
class Solution {
public:
    bool canFinish(int numCourses, vector<pair<int,int> >&prerequisites) {
		
		vector<int> visit;
		vector<GraphNode*> graph;
		for(int i=0;i<numCourses;i++){
			graph.push_back(new GraphNode(i));
			visit.push_back(-1);
		}
		for(int i=0;i<prerequisites.size();i++){
			GraphNode *end   = graph[prerequisites[i].first];
			GraphNode *begin = graph[prerequisites[i].second];	
			begin->neighbors.push_back(end); 
		}
		for(int i=0;i<graph.size();i++){//深度搜索, 
			if(visit[i]==-1&&!DFS_graph(graph[i],visit)){
				return false;//如果节点没有访问国,进行dfs,遇到环 
			}//就返回false 
		}
		for(int i=0;i<numCourses;i++){
			delete graph[i];
		}
		return true;
    }
private:
	bool DFS_graph(GraphNode* root,vector<int> &visit){
		visit[root->label] = 0;
		for(int i=0;i<root->neighbors.size();i++){
			if(visit[root->neighbors[i]->label]==-1){
				if(DFS_graph(root->neighbors[i],visit)==0){
					return false;
				}
			}else if(visit[root->neighbors[i]->label]==0){
				return false;
			}
		}
		visit[root->label]=1;
		return true;
	}
};
//Leetcode自行使用编译器(如DEV\VC\VS)测试部分 
int main(){
	vector<pair<int,int> >prerequisites;
	prerequisites.push_back(make_pair(1,0));
	prerequisites.push_back(make_pair(2,0));
	prerequisites.push_back(make_pair(3,1));
	prerequisites.push_back(make_pair(3,2));
	Solution solve;
	cout<<solve.canFinish(4,prerequisites)<<endl;
	return 0;
}

判断一个无向图是否有环

/**天空树 2019.4.23


有向图不可以使用!!!
 
假定:图顶点个数为M,边条数为E

遍历一遍,判断图分为几部分(假定为P部分,即图有 P 个连通分量)
对于每一个连通分量,如果无环则只能是树,即:边数=结点数-1
只要有一个满足      边数   >   结点数-1
原图就有环
将P个连通分量的不等式相加,就得到:
P1:E1=M1-1
P2:E2=M2-1
...
PN:EN>MN-1
    所有边数(E)   >   所有结点数(M) - 连通分量个数(P)
即:  E + P > M  所以只要判断结果  E  + P > M 就表示原图有环,否则无环.

**/ 
#include  
#include  
using namespace std;  
#define maxNum 100 //定义邻接举证的最大定点数  
int visited[maxNum];//通过visited数组来标记这个顶点是否被访问过,0表示未被访问,1表示被访问  
int DFS_Count;//连通部件个数,用于测试无向图是否连通,DFS_Count=1表示只有一个连通部件,所以整个无向图是连通的  
int pre[maxNum];  
int post[maxNum];  
int point;//pre和post的值  
  
//图的邻接矩阵表示结构  
typedef struct  
{  
    char v[maxNum];//图的顶点信息  
    int e[maxNum][maxNum];//图的顶点信息  
    int vNum;//顶点个数  
    int eNum;//边的个数  
}graph;  
void createGraph(graph *g);//创建图g  
void DFS(graph *g);//深度优先遍历图g  
void dfs(graph *g,int i);//从顶点i开始深度优先遍历与其相邻的点  
void dfs(graph *g,int i)  
{  
    //cout<<"顶点"<v[i]<<"已经被访问"<
    cout<<"顶点"<<i<<"已经被访问"<<endl;  
    visited[i]=1;//标记顶点i被访问  
    pre[i]=++point;  
    for(int j=1;j<=g->vNum;j++)  
    {  
        if(g->e[i][j]!=0&&visited[j]==0)  
            dfs(g,j);  
    }  
    post[i]=++point;  
}  
  
void DFS(graph *g)  
{  
    int i;  
    //初始化visited数组,表示一开始所有顶点都未被访问过  
    for(i=1;i<=g->vNum;i++)  
    {  
        visited[i]=0;  
        pre[i]=0;  
        post[i]=0;  
    }  
    //初始化pre和post  
    point=0;  
    //初始化连通部件数为0  
    DFS_Count=0;  
    //深度优先搜索  
    for(i=1;i<=g->vNum;i++)  
    {  
        if(visited[i]==0)//如果这个顶点为被访问过,则从i顶点出发进行深度优先遍历  
        {  
            DFS_Count++;//统计调用void dfs(graph *g,int i);的次数  
            dfs(g,i);  
        }  
    }  
}  
void createGraph(graph *g)//创建图g  
{  
    cout<<"正在创建无向图..."<<endl;  
    cout<<"请输入顶点个数vNum:";  
    cin>>g->vNum;  
    cout<<"请输入边的个数eNum:";  
    cin>>g->eNum;  
    int i,j;  
    //输入顶点信息  
    //cout<<"请输入顶点信息:"<
    //for(i=0;ivNum;i++)  
    //  cin>>g->v[i];  
    //初始画图g  
    for(i=1;i<=g->vNum;i++)  
        for(j=1;j<=g->vNum;j++)  
            g->e[i][j]=0;  
    //输入边的情况  
    cout<<"请输入边的头和尾"<<endl;  
    for(int k=0;k<g->eNum;k++)  
    {  
        cin>>i>>j;  
        g->e[i][j]=1;  
        g->e[j][i]=1;//无向图对称  
    }  
}  
int main()  
{  
    graph *g;  
    g=(graph*)malloc(sizeof(graph));  
    createGraph(g);//创建图g  
    DFS(g);//深度优先遍历  
    //连通部件数,用于判断是否连通图  
    cout<<"连通部件数量:";  
    cout<<DFS_Count<<endl;  
    if(DFS_Count==1)  
        cout<<"图g是连通图"<<endl;  
    else if(DFS_Count>1)  
        cout<<"图g不是连通图"<<endl;  
    //各顶点的pre和post值  
    for(int i=1;i<=g->vNum;i++)  
        cout<<"顶点"<<i<<"的pre和post分别为:"<<pre[i]<<" "<<post[i]<<endl;  
    //cout<
    //判断无向图中是否有环  
    if(g->eNum+DFS_Count>g->vNum)  
        cout<<"图g中存在环"<<endl;  
    else  
        cout<<"图g中不存在环"<<endl;  
    int k;  
    cin>>k;  
    return 0;  
}  
/* 
输入: 
正在创建无向图... 
请输入顶点个数vNum:10 
请输入边的个数eNum:9 
请输入边的头和尾 
1 2 
1 4 
2 5 
2 6 
4 7 
5 9 
6 3 
7 8 
9 10 
*/  

判断一个有向图是否有环

/**天空树 2019.4.16 

 图中的一个节点,根据其C[N]的值,有三种状态:

    0,此节点没有被访问过

    -1,被访问过至少1次,其后代节点正在被访问中

    1,其后代节点都被访问过。

    按照这样的假设,当按照DFS进行搜索时,碰到一个节点时有三种可能:

    1、如果C[V]=0,这是一个新的节点,不做处理

    2、如果C[V]=-1,说明是在访问该节点的后代的过程中访问到该节点本身,则图中有环。

    3、如果C[V]=1,类似于2的推导,没有环。    在程序中加上一些特殊的处理,即可以找出图中有几个环,并记录每个环的路径


**/ 
#include  
#include  
using namespace std;  
#define maxNum 100 //定义邻接举证的最大定点数  
int pre[maxNum];  
int post[maxNum];  
int point=0;//pre和post的值  
bool is_DAG=true;//标识位,表示有向无环图  
/* 
顶点颜色表 color[u] 
   0 白色,未被访问过的节点标白色 
   -1 灰色,已经被访问过一次的节点标灰色 
   1 黑色,当该节点的所有后代都被访问过标黑色 
反向边: 
   如果第一次访问(u,v)时v为灰色,则(u,v)为反向边。在对图的深度优先搜索中没有发现 
   反向边,则该图没有回路 
程序判断依据: 
    仍然是按图的节点深度遍历,访问到V时,V若被访问过,那么有2种状态: 
    color[u]=-1,程序跳出,存在环 
    color[u]=1,程序继续,这不是环 
时间复杂度:O(n+e) 
*/  
int color[maxNum];//顶点颜色表 color[u]  
//图的邻接矩阵表示结构  
typedef struct  
{  
    char v[maxNum];//图的顶点信息  
    int e[maxNum][maxNum];//图的顶点信息  
    int vNum;//顶点个数  
    int eNum;//边的个数  
}graph;  
void createGraph(graph *g);//创建图g  
void DFS(graph *g);//深度优先遍历图g  
void dfs(graph *g,int i);//从顶点i开始深度优先遍历与其相邻的点  
void dfs(graph *g,int i)  
{  
    //cout<<"顶点"<v[i]<<"已经被访问"<
    cout<<"顶点"<<i<<"已经被访问"<<endl;  
    color[i]=-1;  
    pre[i]=++point;  
    for(int j=1;j<=g->vNum;j++)  
    {  
        if(g->e[i][j]!=0)  
        {     
            if(color[j]==-1)//探索到回边,存在环  
            {  
                is_DAG=false;//不是有向无环图  
            }  
            else if(color[j]==0)  
                dfs(g,j);  
        }  
    }  
    post[i]=++point;  
    color[i]=1;//表示i的后裔节点都被访问过  
}  
void DFS(graph *g)  
{  
    int i;  
    //初始化color数组,表示一开始所有顶点都未被访问过,//初始化pre和post  
    for(i=1;i<=g->vNum;i++)  
    {  
        color[i]=0;  
        pre[i]=0;  
        post[i]=0;  
    }  
    //深度优先搜索  
    for(i=1;i<=g->vNum;i++)  
    {  
        if(color[i]==0)//如果这个顶点为被访问过,则从i顶点出发进行深度优先遍历  
        {  
            dfs(g,i);  
              
        }  
    }  
}  
void createGraph(graph *g)//创建图g  
{  
    cout<<"正在创建有向图..."<<endl;  
    cout<<"请输入顶点个数vNum:";  
    cin>>g->vNum;  
    cout<<"请输入边的个数eNum:";  
    cin>>g->eNum;  
    int i,j;  
    //初始画图g  
    for(i=1;i<=g->vNum;i++)  
        for(j=1;j<=g->vNum;j++)  
            g->e[i][j]=0;  
    //输入边的情况  
    cout<<"请输入边的头和尾"<<endl;  
    for(int k=1;k<=g->eNum;k++)  
    {  
        cin>>i>>j;  
        g->e[i][j]=1;  
    }  
}  
int main()  
{  
    graph *g;  
    g=(graph*)malloc(sizeof(graph));  
    createGraph(g);//创建图g  
    DFS(g);//深度优先遍历  
    //各顶点的pre和post值  
    for(int i=1;i<=g->vNum;i++)  
        cout<<"顶点"<<i<<"的pre和post分别为:"<<pre[i]<<" "<<post[i]<<endl;  
    //判断是否是有向无环图  
    if(is_DAG)  
        cout<<"图g是有向无环图,没有环"<<endl;  
    else  
        cout<<"图g不是有向无环图,存在环"<<endl;  
    int k;  
    cin>>k;  
    return 0;  
}  
/* 
输入1: 
正在创建无向图... 
请输入顶点个数vNum:3 
请输入边的个数eNum:3 
请输入边的头和尾 
1 2 
1 3 
3 2 
输入2: 
正在创建无向图... 
请输入顶点个数vNum:4 
请输入边的个数eNum:4 
请输入边的头和尾 
1 2 
2 3 
3 4 
4 2 
*/ 

你可能感兴趣的:(Leetcode算法刷题笔记,图论,数据结构,算法,c++,leetcode)