LeetCode图(basic)

简单做个刷题记录,涉及到的图的基础知识:邻接表的建立,入度、出度,bfs,dfs

不邻接植花

https://leetcode-cn.com/problems/flower-planting-with-no-adjacent/solution/
思路:本题类似于着色问题,花的种类可以看成颜色的种类。因此建立无向图邻接表vector<vector> Graph(N)存储花园之间的路径,遍历每一个花园并着色,只要保证花园邻接的花园颜色不相同即可。每次遍历时,利用dir{1,2,3,4}存储当前可用的颜色,并查看其邻接点颜色,相同颜色则去除。最后使得路径相连的任何两个花园的颜色,即题目中的花的种类互不相同。

class Solution {
public:
    vector<int> gardenNoAdj(int N, vector<vector<int>>& paths) {
        //建立无向图的邻接表
         vector<vector<int>> Graph(N);
        for(int i = 0;i<paths.size(); i++){
            Graph[paths[i][0]-1].push_back(paths[i][1]-1);
            Graph[paths[i][1]-1].push_back(paths[i][0]-1);
        }
        vector<int> answer(N,0);//初始化,0代表未染色
        for(int i = 0;i<N; i++){
            set<int> dir{1,2,3,4};//存放颜色
            //通过set的erase用法保证路径相连的花园中的花的种类不同
            for(int j = 0;j<Graph[i].size();j++){
                dir.erase(answer[Graph[i][j]]);//相同颜色则去除掉
            }
            answer[i]=*(dir.begin());
        }
        return answer;
    }
};
找到小镇的法官

https://leetcode-cn.com/problems/find-the-town-judge/
思路:把小镇上的人看成图的一个个顶点,法官就是满足入度为N-1,出度为0的点。因此,用一个degree存储每一个顶点的入度和出度的差值。遍历trust数组,每次入度记为-1,出度记作+1,最后遍历degree数组,元素的值为N-1即为所求的法官,否则不存在法官满足题意。

class Solution {
public:
    int findJudge(int N, vector<vector<int>>& trust) {
        vector<int> degree(N+1,0);
        for(int i = 0;i<trust.size();i++){
            degree[trust[i][0]]--;//出度-1
            degree[trust[i][1]]++;//入度+1
        }
        for(int i =1;i<=N;i++){
            if(degree[i]==N-1)
                return i;
        }
        return -1;
    }
};
所有可能的路径

https://leetcode-cn.com/problems/all-paths-from-source-to-target/
思路:典型的dfs模板题。从起点0开始,对图进行深度优先遍历,用path记录每次尝试的路径经过的节点,当有一条路径可以抵达终点N-1时,把该路径加入res数组中

class Solution {
public:
    vector<vector<int>>res;
    vector<int> path;
    int len;
    void dfs(int x,vector<vector<int>>& graph,int visited[]){
        visited[x] = 1;//标记该点已经在路径中了
        path.push_back(x);
        if(x==len-1){//判断是否到达目标点
            res.push_back(path);
        }else{
            int size = graph[x].size();
            for(int i = 0;i<size;i++){
                dfs(graph[x][i],graph,visited);
            }
        }
        path.pop_back();
        visited[x] = 0;//之前的路径探索完毕之后,取消对该节点的标记
    }
    vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
        len = graph.size();
        int visited[16]={0};
        dfs(0,graph,visited);
        return res;
    }
};
克隆图

https://leetcode-cn.com/problems/clone-graph/
思路:采用BFS方式对图进行遍历,unoredered_map<Node*,Nodes*> visited是避免重复访问子节点。visited的key 是原来图中的节点,value 是克隆图中的对应节点,即A->A1,B->B1,C->C1,D->D1……,如果某个节点已经被访问过,则直接将该节点加入到邻居列表中,如果没有被访问过,则构造新的节点对它的邻居列表循环执行访问操作。

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> neighbors;
    
    Node() {
        val = 0;
        neighbors = vector<Node*>();
    }
    
    Node(int _val) {
        val = _val;
        neighbors = vector<Node*>();
    }
    
    Node(int _val, vector<Node*> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
};
*/
class Solution {
public:
    void dfs(Node* node,vector<Node *>&neigh,unordered_map<Node*, Node*>&visited){
        if(neigh.empty())
            return;
        for(int i = 0;i<neigh.size();i++){
            if(!visited.count(neigh[i])){
                //该节点没有被克隆过,构造新的节点
                Node* pNew = new Node(neigh[i]->val,vector<Node*>{});
                visited[neigh[i]] = pNew;
                node->neighbors.push_back(pNew);
                dfs(pNew,neigh[i]->neighbors,visited);
            }else{
                //如果该节点已经被访问过,则把它加到邻居列表中
                node->neighbors.push_back(visited[neigh[i]]);
            }
        }
    }
    Node* cloneGraph(Node* node){
        if(node==NULL){
            return nullptr;
        }
        unordered_map<Node*, Node*>visited;
        Node *copy = new Node(node->val);
        visited[node] = copy;
        dfs(copy,node->neighbors,visited);
        return copy;
    }
};
找到最终的安全状态

https://leetcode-cn.com/problems/find-eventual-safe-states/
思路:从题意可知,不安全的点绝对成环,因此利用深度搜索判断哪些点不在环上,即为所求的点。定义节点的访问状态:用0表示未访问过,1表示访问过但不确定是否是环上的点,2表示安全的点。每次访问将节点标记为1,如果在搜索过程中我们遇到节点的状态也为1(表示已经被访问过了),那么说明找到了一个环,此时退出搜索,如果搜索过程中,我们没有遇到成环的点,那么标记该点为安全的点。

class Solution {
public:
//用0表示未访问过,1表示访问过但不确定是否是环上的点,2表示安全的点
    bool dfs(vector<vector<int>>& graph,vector<int>& visited,int n){
        if(visited[n]!=0){
            return visited[n]==2;
        }
        visited[n] = 1;
        for(int i=0;i<graph[n].size();i++){
            if(visited[graph[n][i]]==1||dfs(graph,visited,graph[n][i])==false){
                return false;
            }
        }
        visited[n] = 2;
        return true;
    }
    vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
        int N = graph.size();
        vector<int> visited(N,0);
        vector<int> res;
        for(int i = 0;i<N; i++){
            if(dfs(graph,visited,i)){
                res.push_back(i);
            }
        }
        return res;
    }
};
课程表

https://leetcode-cn.com/problems/course-schedule/
思路:注意题目信息,完成课程A需要先完成课程B(B->A),完成课程B需要先完成课程A(A->B),这样就形成了一个循环,因此可以联想到一种类型题:判断有向图中是否存在环。把课程表构造成一个有向图,如果课程能够全部完成,有向图中就一定没有环。通过深度优先搜索遍历图中所有的点,来查找是否有环存在。跟上面一题(找到最终的安全状态)的思路是一样的,因此把上面一题的代码改写一下就可以了~~

class Solution {
public:
    bool dfs(vector<vector<int>>& graph,vector<int>& visited,int n){
        if(visited[n]!=0){
            return visited[n]==2;
        }
        visited[n] = 1;
        for(int i = 0;i<graph[n].size();i++){
            if(visited[graph[n][i]]==1||dfs(graph,visited,graph[n][i])==false)
                return false;
        }
        visited[n]=2;
        return true;
    }
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int> >graph(numCourses,vector<int>(0));
        for(int i = 0;i<prerequisites.size();i++){
            //学0之前得先完成1,那么就是1->0
            graph[prerequisites[i][1]].push_back(prerequisites[i][0]);
        }
        //图的标签 0-numCourse-1
        vector<int> visited(numCourses,0);
        for(int i = 0;i<numCourses;i++){
            if(!dfs(graph,visited,i))
                return false;
        }
        return true;
    }
};
最小高度树

https://leetcode-cn.com/problems/minimum-height-trees/
思路:BFS。本题中为了使得树的高度最小,root节点应该是尽量靠近中心节点(可以理解为离所有叶子节点相对较近的点)因此如果将最外层的叶子节点去除掉,每去一次就会有新的一层叶子节点出现,这样一直去除下去直到最后一层就是叶子节点
代码实现:定义degree[n]的数组,存储每个节点的度数,每次将度数为1的节点(叶子节点)入队列,循环去掉叶子节点部分,直到不能再去除为止
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
        if(n==0)
            return {};
        else if(n==1)
            return {0};
        else if(n==1)
            return {0,1};
        
        vector<int> graph[n];
        int degree[n];
        memset(degree,0x00,sizeof(int)*n);
        for(int i = 0;i<edges.size();i++){
            graph[edges[i][0]].push_back(edges[i][1]);
            graph[edges[i][1]].push_back(edges[i][0]);
            degree[edges[i][0]]++;
            degree[edges[i][1]]++;
        }
        queue<int> q;
        for(int i = 0;i<n;i++){
            if(degree[i]==1){
                q.push(i);//把叶子节点添加进队列中
            }
        }
        vector<int> res;
        while(!q.empty()){
            res.clear();
            int len = q.size();
            for(int i = 0;i<len;i++){
                int node = q.front();
                res.push_back(node);
                q.pop();
                for(int j = 0;j<graph[node].size();j++){
                    degree[graph[node][j]]--;//删除叶子节点后,跟其相邻的节点的度数要减少
                    if(degree[graph[node][j]]==1)
                        q.push(graph[node][j]);
                }
            }
        }
        return res;
    }
};
判断二分图

https://leetcode-cn.com/problems/is-graph-bipartite/
思路:BFS。由二分图的特点可以知道两个不同的顶点集合之间存在边,而每个顶点集合内部并没有边。因此,如果用两种颜色给顶点染色,那么相同集合的顶点染成相同颜色即可。例如,先找到一个有边的顶点v0,并给v0的邻接点v1,v2染上不同的颜色时,同理,再给v1,v2的邻接点v3,v4,v5染上v0的颜色,依次循环下去。在广度优先遍历过程中,若发现邻接点已经被染色且颜色与当前点相同,那么便不是二分图。
题目中的无向图不一定是连通,因此需要多次遍历,保证每一个节点都被染色或确定不是二分图为止。

class Solution {
public:
    bool isBipartite(vector<vector<int>>& graph) {
        int len = graph.size();
        vector<int> visited(len,0);
        queue<int> q;
        for(int i = 0;i<len;i++){
            if(visited[i]!=0)//已经被访问过
                continue;
            visited[i]=1;
            q.push(i);
            while(!q.empty()){
                int num = q.front();
                q.pop();
                int flag = visited[num];
                for(int j = 0;j<graph[num].size();j++){
                    if(visited[graph[num][j]]==0){//没有被访问过
                        q.push(graph[num][j]);
                        visited[graph[num][j]]=-flag;
                    }else if(visited[graph[num][j]]==flag){
                        return false;
                    }
                }
            }
        }
        return true;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值