zoukankan      html  css  js  c++  java
  • Leetcode #785 判断二分图

    题名:判断二分图
    描述:
    给定一个无向图graph,当这个图为二分图时返回true。

    如果我们能将一个图的节点集合分割成两个独立的子集A和B,并使图中的每一条边的两个节点一个来自A集合,一个来自B集合,我们就将这个图称为二分图。

    graph将会以邻接表方式给出,graph[i]表示图中与节点i相连的所有节点。每个节点都是一个在0到graph.length-1之间的整数。这图中没有自环和平行边: graph[i] 中不存在i,并且graph[i]中没有重复的值。

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/is-graph-bipartite/

    方法:DFS + 二分图概念

    什么是二分图:如果对图中每一个顶点上色(只有2种颜色),如果任一顶点的相邻顶点颜色与其不同,则称改图为二分图。

    DFS用于遍历图的各个顶点,用0表示黑色,1表示蓝色,顶点初始值为-1表示无色,然后模拟上色过程即可。
    有一点需要注意,如果用vector来存储colors很容易超时,用int数组来存就不会。

    
        bool dfs(vector<vector<int>>& graph, int *colors, int curNode, int curColor)
        {
            colors[curNode] = curColor;//上色
            for(auto w : graph[curNode])
            {
                if(colors[w] == -1){//如果没上过色
                    if(!dfs(graph, colors, w, 1 - curColor))
                        return false;
                }  
                else if(colors[w] == curColor)  return false;//如果颜色与相邻顶点相同,返回false       
            }
            return true;
        }
        bool isBipartite(vector<vector<int>>& graph) {
            int colors[110];//表示是顶点i的颜色
            fill(colors, colors + 110, -1);//初始化为未上色
            for(int i = 0;i < graph.size();i++)//防止出现非连通图
            {
                if(colors[i] == -1 && !dfs(graph, colors, i, 0))
                    return false;
            }
            return true;
        }
    

    Leetcode #684 冗余连接

    题名:冗余连接
    描述:
    在本问题中, 树指的是一个连通且无环的无向图。

    输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, ..., N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。

    结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。

    返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v。

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/redundant-connection/

    这道题有两个思路,一是用DFS来遍历整个图并记录改图的联通分量,当连通分量仅为1时即找到了答案;第二种思路是用并查集;

    方法一:

    利用DFS遍历图并记录联通分量,常规操作,上代码:

        int maxn = 1000;
        int G[maxn][maxn];
        vector<int> findRedundantConnection(vector<vector<int>>& edges) {
            int vn = edges.size(), num;
            fill(G[0], G[0] + vn, 0);
            bool vis[vn]{false};//初始化vis为false
            int index = vn;
            for(int i = 0;i < vn;i++){//将图存储在邻接矩阵中
                G[edges[i][0] - 1][edges[i][1] - 1] = 1;
                G[edges[i][1] - 1][edges[i][0] - 1] = 1;
            }
            while(num != 1 && index-- >= 0){//从后往前遍历vector来找到正确的边,当删去该边时使得图的连通分量为1;(以防万一让index >= 0)
                int u = edges[index][0] - 1, v = edges[index][1] - 1;
                fill(vis, vis + vn, false);
                G[u][v] = 0;//删去对应的边
                G[v][u] = 0;//删去对应的边
                num = dfs(vn, vis);
                G[u][v] = 1;//添上对应的边
                G[v][u] = 1;//添上对应的边
            }
            return edges[index];
        }
        int dfs(int vn, bool *vis){
            int res = 0;
            for(int i = 0;i < vn;i++){
                if(!vis[i]){
                    res++;//用于记录连通分量
                    dfs(vn, i, vis);
                }
            }
            return res;
        }
        void dfs(int vn, int v, bool *vis){
            vis[v] = true;
            for(int i = 0;i < vn;i++){
                if(!vis[i] && G[v][i] == 1) dfs(vn, i, vis);
            }
        }
    

    方法二:

    用并查集的思路,首先来回顾一下并查集:
    1.并查集有什么用呢?
    它最大的作用就是用来检查一个图是否存在环。
    2.并查集的操作分两步:一、并(Union) 二、查(Find)
    常规写法:

        int maxn = 1001;
        int fa[maxn];
        int Find(int x){
            int a = x;
            while(x != fa[x]){
                x = fa[x];
            }//如果x的父亲不是它自己本身
            //对集合进行优化:
            while(a != fa[a]){
                int z = a;
                a = fa[a];
                fa[z] = x;
            }
            return x;
        }
    
        void Union(int a, int b){
            int af = Find(a);
            int bf = Find(b);
            if(af != bf)    fa[a] = bf;
        }
    

    所以这题的完整代码如下:

    
    vector<int> findRedundantConnection(vector<vector<int>>& edges){
            int vn = edges.size();
            int fa[vn + 1];
            vector<int> res;
            for(int i = 1;i <= vn;i++){
                fa[i] = i;
            }//初始化fa数组,使每个顶点的父亲都是其本身
            for(int i = 0;i < vn;i++){
                int u = edges[i][0], v = edges[i][1];
                if(!Union(fa, u, v)){//如果合并失败说明图中出现了环,所以这条边就是产生环的边,就是答案
                    res.push_back(u);
                    res.push_back(v);
                    break;
                }
            }
            return res;
        }
        int Find(int *fa, int x){
            int a = x;
            while(x != fa[x]){
                x = fa[x];
            }
            while(a != fa[a]){
                int z = a;
                a = fa[a];
                fa[z] = x;
            }
            return x;
        }
        bool Union(int *fa, int a, int b){
            int af = Find(fa, a);
            int bf = Find(fa, b);
            if(af != bf){
                fa[af] = bf;
                return true;
            }
            return false;
        }
    

    Leetcode #133 克隆图

    题名:克隆图
    描述:
    给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。

    图中的每个节点都包含它的值 val(int) 和其邻居的列表(list[Node])。

    测试用例格式:

    简单起见,每个节点的值都和它的索引相同。例如,第一个节点值为 1,第二个节点值为 2,以此类推。该图在测试用例中使用邻接列表表示。

    邻接列表是用于表示有限图的无序列表的集合。每个列表都描述了图中节点的邻居集。

    给定节点将始终是图中的第一个节点(值为 1)。你必须将 给定节点的拷贝 作为对克隆图的引用返回。

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/clone-graph/

    EXAMPLE:
    
    输入:adjList = [[2,4],[1,3],[2,4],[1,3]]
    输出:[[2,4],[1,3],[2,4],[1,3]]
    解释:
    图中有 4 个节点。
    节点 1 的值是 1,它有两个邻居:节点 2 和 4 。
    节点 2 的值是 2,它有两个邻居:节点 1 和 3 。
    节点 3 的值是 3,它有两个邻居:节点 2 和 4 。
    节点 4 的值是 4,它有两个邻居:节点 1 和 3 。
    

    方法: DFS

    用DFS遍历每个图节点,new一个新对象并通过构造函数进行val上的复制,然后继续DFS并将新生成的结点存储到邻接表中。代码如下:

    
        bool vis[105] = {false};
        Node* cloneGraph(Node* node) {
            if(!node)   return NULL;
            map<int, Node *> mp;//因为val唯一标识一个结点,所以用map存储val值到结点指针的映射;
            Node *ans = NULL;
            ans = dfs(node, mp);
            return ans;
        }
    
        Node *dfs(Node* node, map<int, Node *> &mp){
            vis[node->val] = true;
            Node *nn = new Node(node->val);//注意是深拷贝,不能用 Node(node->val, node->neighbor) 这个构造函数
            mp[node->val] = nn;//存储映射
            for(int i = 0;i < node->neighbors.size();i++){
                Node *tmp = node->neighbors[i];//初始化tmp为邻接表中的图结点指针
                if(!vis[tmp->val]){//如果没访问过,也就是该节点还没new过,那么去new这个节点
                    tmp = dfs(tmp, mp);
                    nn->neighbors.push_back(tmp);
                }
                else//如果已经new过该节点,将该节点存储到邻接表中
                    nn->neighbors.push_back(mp[tmp->val]);
            }
            return nn;
        }
    

    Leetcode #130 被围绕的区域

    题名:被围绕的区域
    描述:
    给定一个二维的矩阵,包含 'X' 和 'O'(字母 O)。

    找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。

    示例:

    X X X X
    X O O X
    X X O X
    X O X X
    

    运行你的函数后,矩阵变为:

    X X X X
    X X X X
    X X X X
    X O X X
    

    解释:
    被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/surrounded-regions/

    方法:DFS

    想法:只需要找出图的边界上O,以及与这个O相连接的区域,将该区域保留,其他区域都置为X即可。
    1.对边界上字母为O的点开始DFS。
    2.将DFS遍历到的字母置为N
    3.结束DFS后,对矩阵进行从上到下遍历,将字母为N的点置为O,其他点置为X

    代码如下:

        bool vis[1000][1000];
        void solve(vector<vector<char>>& board) {
            if(!board.size())   return;
            int x = board.size(), y = board[0].size();
            for(int i = 0;i < x;i++){
                if(i == 0 || i == x - 1){
                    for(int j = 0;j < y;j++){
                        if(!vis[i][j] && board[i][j] == 'O')
                            dfs(board, i, j, x, y);
                    }
                }
                else{
                    if(!vis[i][0] && board[i][0] == 'O')
                        dfs(board, i, 0, x, y);
                    if(!vis[i][y - 1] && board[i][y - 1] == 'O')
                        dfs(board, i, y - 1, x, y);
                }
            }
            for(int i = 0;i < x;i++){
                for(int j = 0;j < y;j++){
                    if(board[i][j] == 'N')
                        board[i][j] = 'O';
                    else if(board[i][j] == 'O')
                        board[i][j] = 'X';
                }
            }
        }
        void dfs(vector<vector<char>>& board, int u, int v, int x, int y){
            vis[u][v] = true;
            board[u][v] = 'N';
            //将与顶点[u,v]相邻的(上下左右)且字母为 O 的点加入容器中,以备之后DFS使用。
            vector<pair<int, int> > vi;
            if(u - 1 >= 0 && board[u - 1][v] == 'O')
                vi.push_back(make_pair(u - 1, v));
            if(u + 1 < x && board[u + 1][v] == 'O')
                vi.push_back(make_pair(u + 1, v));
            if(v - 1 >= 0 && board[u][v - 1] == 'O')
                vi.push_back(make_pair(u, v - 1));
            if(v + 1 < y && board[u][v + 1] == 'O')
                vi.push_back(make_pair(u, v + 1));
            for(int i = 0;i < vi.size();i++){
                int a = vi[i].first, b = vi[i].second;
                if(!vis[a][b])
                    dfs(board, a, b, x, y);
            }
        }
    

    Leetcode #13 机器人的运动范围

    题名:机器人的运动范围
    描述:
    地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

    示例1:

    输入:m = 2, n = 3, k = 1
    输出:3
    

    示例2:

    输入:m = 3, n = 1, k = 0
    输出:1
    

    提示:

    1 <= n,m <= 100
    0 <= k <= 20
    

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof/

    方法:直接DFS

    代码如下:

    
    ool vis[101][101] = {false};
        int movingCount(int m, int n, int k) {
            //vector<vector<bool> > vis(m, vector<bool> (n, false));
            return dfs(0, 0, m, n, k);
        }
        int dfs(int u, int v, int m, int n, int k){
            if(u < 0 || v < 0 || u >= m || v >= n || vis[u][v] ||
                u % 10 + u / 10 + v % 10 + v / 10 > k)
                return 0;
            vis[u][v] = true;
            return dfs(u - 1, v, m, n, k) +
                   dfs(u + 1, v, m, n, k) +
                   dfs(u, v - 1, m, n, k) +
                   dfs(u, v + 1, m, n, k) + 1;
        }
    
  • 相关阅读:
    java通过sort()方法实现数组和集合升序降序排列
    基本数据类型和它们的包装类
    final和static关键字
    对象的回收
    栈和堆
    成员变量和局部变量
    Nginx服务器的Websockets配置方法
    failed: Error during WebSocket handshake: Unexpected response code: 404
    使用Nginx做WebSockets代理教程
    详解Nginx反向代理WebSocket响应403的解决办法
  • 原文地址:https://www.cnblogs.com/Codroc/p/12355879.html
Copyright © 2011-2022 走看看