连通子图问题(DFS的递归和非递归实现)

2023-05-16

问题定义(以下均为Java实现)

  输入一个 m m m n n n列的字符矩阵, 统计字符“@”组成多少个八连块。 如果两个字符“@”所在的格子相邻( 横、 竖或者对角线方向) , 就说它们属于同一个八连块。 例如, 下图有 3 3 3个八连块。
在这里插入图片描述
解题思路:深/广度优先遍历,记录已经遍历过的字符。深度优先遍历有不同的实现,下面是非递归(栈)或者递归的两种解法。

解法一

  基于栈的DFS,当遍历到某个字符时,入栈并标记该字符,然后继续判断该字符的相邻字符,当该字符没有相邻字符为“@”则出栈。代码如下:

  1. 首先构造一个描述图节点的类Dot,包含实例属性r,c分别表示该字符所在行,所在列。其中关于get、set以及构造方法已经省略,此外为了判断是否已经遍历过,重写了equals和hashCode方法。
class Dot{
        private int r;
        private int c;
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Dot dot = (Dot) o;
            return getR() == dot.getR() &&
                    getC() == dot.getC();
        }

        @Override
        public int hashCode() {
            return Objects.hash(getR(), getC());
        }

    }
  1. 下面是算法实现:
      方法有一个参数二维数组graph,即字符图。
      集合exists用于保存已经遍历过的字符。首先是两层循环(多个八连块),遍历到某一字符时,如果该字符为“@”且未遍历过(则意味着新的八连块出现),则以当前节点为根进行DFS,这里声明了一个栈,根节点首先入栈,并加入exists中,然后判断其周围是否有符合条件的字符,有则入栈并继续进行DFS,否则执行出栈,最后直至栈为空,则当前的一个八连块完毕,继续下一个八连块,直至整个图都遍历完毕,程序最后返回图中八连块的个数。
    /**
     * @param graph 图
     * @return 八连块个数
     */
    private int solver1(char[][] graph){
        if(graph == null){
            return 0;
        }
        Set<Dot> exists = new HashSet<>();
        int rnum = graph.length;
        int cNum = graph[0].length;
        int count = 0;

        for(int i = 0;i<rnum;i++){
            for(int j = 0;j<cNum;j++){
                Dot dot = new Dot(i,j);
                if(graph[i][j] == '@' && !exists.contains(dot)){
                    Stack<Dot> stack = new Stack<>();
                    stack.push(dot);
                    exists.add(dot);
                    while(!stack.isEmpty()){
                        Dot cur = stack.peek();
                        int r1 = Math.max(0, cur.getR()-1);
                        int r2 = Math.min(cur.getR()+1, rnum-1);
                        int c1 = Math.max(0, cur.getC() - 1);
                        int c2 = Math.min(cur.getC()+1, cNum-1);
                        /* flag:当存在相邻且未遍历的字符“@”时,需要跳出循环*/
                        boolean flag = false;
                        for(int r = r1;r<=r2;r++){
                            for(int c = c1;c<=c2;c++){
                                Dot d = new Dot(r,c);
                                if(graph[r][c] == '@' && !exists.contains(d)){
                                    stack.push(d);
                                    exists.add(d);
                                    flag = true;
                                    break;
                                }
                            }
                            if(flag){
                                break;
                            }
                        }
                        /* 如果不存在相邻且未遍历的@字符,则出栈 */
                        if(cur == stack.peek()){
                            stack.pop();
                        }
                    }
                    count++;
                }
            }
        }
        return count;
    }

解法二

  基于递归的DFS,对于当前节点,首先判断是否越界,是否为“@”,是否已经遍历过,否则符合条件,标记当前节点属于哪个八连块(这里二维数组exists用于记录节点属于哪个八连块),然后判断周围字符。

    /**
     *
     * @param graph 图
     * @param r 字符纵坐标
     * @param c 字符横坐标
     * @param id 八连块序号
     * @param exists 判断是否已经遍历过
     */
    private void solver2(char[][] graph, int r, int c, int id, int[][] exists){
        if(r<0 || c<0 || r>=graph.length || c>=graph[0].length){
            return ;
        }
        if(exists[r][c] != 0 || !(graph[r][c]=='@')){
            return;
        }
        exists[r][c] = id;
        for(int i  = -1;i<2;i++){
            for(int j = -1;j<2;j++){
                if(i != 0 || j != 0){
                    solver2(graph, r+i, c+j, id, exists);
                }
            }
        }
    }

辅助输入输出的代码如下所示:

public class OilDeposits {
	private int solver1(char[][] graph){{/*代码在上面*/}
	private void dfs(char[][] graph, int r, int c, int id, int[][] exists){/*代码在上面*/}
	public void solution(char[][] graph){
        System.out.println("解法一:" + solver1(graph));


        int[][] exists = new int[graph.length][graph[0].length];
        int count = 0;
        for(int i = 0;i<graph.length;i++){
            for(int j = 0;j<graph[0].length;j++){
                if(graph[i][j] == '@' && exists[i][j] == 0){
                    solver2(graph, i, j, ++count, exists);
                }
            }
        }
        System.out.println("解法二:" + count);
    }
    public static void main(String[] args){
        char graph[][] = {
                {'*','*','*','*','*','@',},
                {'*','*','@','@','*','@',},
                {'*','*','@','*','*','@',},
                {'@','@','@','*','*','@',},
                {'*','*','@','*','*','@',},
                {'*','*','*','*','*','@',},
                {'*','*','@','@','*','@',},};
        OilDeposits oilDeposits = new OilDeposits();
        oilDeposits.solution(graph);
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

连通子图问题(DFS的递归和非递归实现) 的相关文章

  • 数据结构PTA 案例6-1.4 地下迷宫探索

    案例6 1 4 地下迷宫探索 题目 解法 题目 假设有一个地下通道迷宫 它的通道都是直的 而通道所有交叉点 包括通道的端点 上都有一盏灯和一个开关 请问你如何从某个起点开始在迷宫中点亮所有的灯并回到起点 输入格式 输入第一行给出三个正整数
  • D - 整数变换问题

    整数变换问题 题意 问我们最少经过多少次变换可以将n转化为m 题解 这个题我们很容易想到就是用dfs 但是数据范围也很明显不能用直接的暴力 所以我们需要剪枝 我们假设用最原始的暴力 就是每次循环两种情况一直到最后 这样的暴力很机械 很盲目
  • 寻找 有向图/无向图 所有环路的DFS暴力求解法(ps:C++代码,复杂度爆炸警告,生产环境慎用)

    思路 1 DFS算法可以求解图中从一点到另一点的全部路径 2 通过枚举所有顶点的邻接点 然后通过DFS寻找枚举点到的所有路径来寻找环路 3 思路很简单 但是算法复杂度确实是太高了 下面上代码 include
  • UVA1613 K-GraphOddity

    UVA1613 K GraphOddity 题目传送门 刚看第一眼一点思路都没有 后面看了大佬的题解发现这道题其实是一道水题 用到的方法就是DFS遍历图 我是废物 题目意思很简单 就不分析了 下面直接说方法 首先求出k 然后dfs遍历一遍图
  • 力扣刷题-47.全排列Ⅱ、深度优先搜索

    给定一个可包含重复数字的序列 返回所有不重复的全排列 深度优先搜索 DFS 深度优先搜索就是在每一步对每一种可能的选择一条道走到底 然后再回过头尝试另外一种选择 深度优先搜索的关键是要考虑 当前这一步 该如何做 至于 下一步 该怎么做和当前
  • 编程训练————岛屿数量(C++)

    岛屿数量 题目描述 主要思想 深度优先搜索 广度优先搜索 代码实现 深度优先搜索 广度优先搜索 题目描述 给你一个由 1 陆地 和 0 水 组成的的二维网格 请你计算网格中岛屿的数量 岛屿总是被水包围 并且每座岛屿只能由水平方向或竖直方向上
  • 算法---LeetCode 200. 岛屿数量

    1 题目 原题链接 给你一个由 1 陆地 和 0 水 组成的的二维网格 请你计算网格中岛屿的数量 岛屿总是被水包围 并且每座岛屿只能由水平方向和 或竖直方向上相邻的陆地连接形成 此外 你可以假设该网格的四条边均被水包围 示例 1 输入 gr
  • 洛谷题单 算法1-7 搜索

    USACO08FEB Meteor Shower S 题目描述 Bessie hears that an extraordinary meteor shower is coming reports say that these meteor
  • 路径搜索问题

    之前碰到的很多问题都可以归结为路径搜索问题 就是求两点之间的路经 1 是否存在路径 2 求任意一条路径 3 求所有路径 求是否有路径和任意一条路径的时候 和正常遍历一样 一个点被mark之后不再访问 因为如果这个结点到终点有路径 之前就应该
  • 02 二叉树的DFS(前序、中序或后序遍历实现)【Binary Tree 二叉树】

    二叉树的深度优先遍历主要有三种 前序 根左右 中序 左根右 后序 左右根 下面是完整的实现和讲解 include
  • [LeetCode] Binary Tree Level Order Traversal 二叉树层次遍历(DFS

    目录 1 Binary Tree Level Order Traversal 二叉树层次遍历 BFS 2 Binary Tree Level Order Traversal II 二叉树层次遍历从低往高输出 BFS 3 Maximum De
  • 组合、子集问题汇总

    子集的问题的思路也分两个方向 一种是解空间树是关于每个数选还是不选 结点取值范围就是true or false 解向量的长度是固定的 即candidates的个数 而且只有完全解才是问题的解 解向量不是直接的答案 而是标志每个candida
  • 剪格子 蓝桥杯 211

    题目描述 如下图所示 3 x 3 的格子中填写了一些整数 我们沿着图中的红色线剪开 得到两个部分 每个部分的数字和都是 60 本题的要求就是请你编程判定 对给定的 m n 的格子中的整数 是否可以分割为两个部分 使得这两个区域的数字和相等
  • poj 3074 Sudoku

    Time Limit 1000MS Memory Limit 65536K Total Submissions 7613 Accepted 2696 Description In the game of Sudoku you are giv
  • 蓝桥杯2019年c++b组国赛题目及题解

    填空题目来源来自于 https blog csdn net l503301397 article details 90697079 大题来源于 ACwing https www acwing com problem search 2 csr
  • Knight Moves_dfs_2018_3_10

    A friend of you is doing research on the Traveling Knight Problem TKP where you are to find the shortest closed tour of
  • 链式前向星存树图和遍历它的两种方法【dfs、bfs】

    目录 一 链式前向星存图 二 两种遍历方法 一 链式前向星存图 n个点 n 1条边 链式前向星把上面的树图存下来 输入 9 代表要存进去n个点 1 2 下面是n 1条边 每条边连接两个点 1 3 1 7 2 4 4 5 4 6 3 8 3
  • 数据结构——图的DFS(深度优先遍历)- C语言代码实现

    图的深度优先遍历的基本思想 从图中某顶点v出发 1 访问顶点v 2 依次从v的未被访问的邻接点出发 对图进行深度优先遍历 直至图中和v有路径相通的顶点都被访问 3 若此时图中尚有顶点未被访问 则从一个未被访问的顶点出发 重新进行深度优先遍历
  • P1433 吃奶酪 题解(勿抄袭)

    P1433 吃奶酪 题目描述 房间里放着 n 块奶酪 一只小老鼠要把它们都吃掉 问至少要跑多少距离 老鼠一开始在 0 0 点处 输入格式 第一行一个正整数 n 接下来每行 2 个实数 表示第i块奶酪的坐标 两点之间的距离公式为 输出格式 一
  • DFS的个人理解和测试例题

    深度优先搜索 DFS 是一种搜索手段 可以理解为 它从某个位置 起点 开始 沿着一条路不断地向前走直到尽头 然后退后一步 去走其它没走过的路 没有的话 再退后一步 再去选择 直到找到目的地 终点 例如下图 从A 起点 开始走 先走ABD 在

随机推荐