DFS算法

DFS算法

DFS算法即:Depth First Search,深度优先搜索。这个算法的关键是解决“当下如何做”,至于下一步如何做和“当下如何做”是一样的,该算法从一个状态DFS(n)转移到下一个状态DFS(n+1),直到状态无法转移即到达临界点,然后回退到上一个状态,在上一个状态的基础上继续遍历其他状态,如此不断重复,直到找到最终解。如果算法有M个状态,每个状态有N种可能可以尝试,那么总的尝试次数是N^M 即M个N相乘。

算法的关注点:

  1. 当下如何做,当下的状态如何处理
  2. 如何转移到下一个状态,下一个状态有哪些可能?
  3. 临界条件设定和dfs结束条件的设定以及处理
  4. 标志位的设置和复位,标记资源被占用,以及当前使用后及时释放,留给下一次使用。

一般在进行DFS遍历的时候,会有一些限制条件,如已经用过的东西不能再使用,则需要对每次的使用做一个标记,然后转入到下一状态,并且从下一状态返回时候需要清除标记。

关键是分清状态和每种状态下的可能的区别。

全排列问题

如求数字1到N的全排列,准备N个盒子,每个盒子可以放入1~N中的任一数字,但是在放入数字的时候,不能放已经使用过的数字。当N个盒子放完后即完成一次全排列。
即有N种状态,每遍历一个盒子即为一个状态,遍历到最后一个盒子时候状态无法转移了,需要回退,然后在每个状态,又有N种可能可以尝试,即每个盒子可以放入1~N中的任一数字。

代码如下:

public class Test01 {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        dfsPermutation(0);
    }

    static int N = 6;//1~N的全排列
    static int[] box = new int[N];//用来放N个排列的数字
    static int[] book = new int[N+1];//对使用过的数字进行标记,数字即下标

    //对当下状态的处理,即N种可能尝试,每个盒子可以放入1~N中的任一数字
    //index 即数组box的下标,从0开始进行dfs遍历,直到N-1有效
    static void dfsPermutation(int index) {
        //达到临界条件,输出结果,中断遍历,返回上一状态
        if (index == N) {
            System.out.println(Arrays.toString(box));
            return;
        }
        
        //每个盒子可以放入1~N的数字,每个状态有N次尝试
        for (int i=1; i<=N; i++) {
            if (book[i] == 0) {//放入数字的时候有限制条件,只能使用未用过的数字i,未用过的数字的标记位 book[i]=0
                box[index] = i; //把数字 i 放入到box数组,生成全排列
                book[i] = 1;//标记数字i 已经被使用了
                dfsPermutation(index+1); //进入到下一次转移
                
                book[i] = 0;//从上一个状态返回时,清空i的使用标志,进行下一个尝试
            }
        }
    }   
}

迷宫问题

定义一个二维数组N*M(其中2<=N<=10;2<=M<=10),如5 × 5数组它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左
上角到右下角的最短路线。入口点为[0,0],既第一空格是可以走的路。
Input
一个N × M的二维数组,表示一个迷宫。数据保证有唯一解,不考虑有多解的情况,即迷宫只有一条通道。
Output
左上角到右下角的最短路径,格式如样例所示。
Sample Input
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
Sample Output
(0, 0)
(1, 0)
(2, 0)
(2, 1)
(2, 2)
(2, 3)
(2, 4)
(3, 4)
(4, 4)

编程实现:

public class Test01 {
    static int[][] maze1 = {
            {0, 1, 0, 0, 0},
            {0, 1, 0, 1, 0},
            {0, 0, 0, 0, 0},
            {0, 1, 1, 1, 0},
            {0, 0, 0, 1, 0}};
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);        
            int[][] book = new int[6][6];
            dfs(maze1, book, 0, 0, 1);
            printPoints(points);
            
    }
    

    static int[][] next = {{0,1},{0,-1},{1,0},{-1,0}};//4个方向

    static int[][] points;
    // steps 第几个状态 从1开始
    static void dfs(int[][] map, int[][] book, int x, int y, int steps) {
        
        //到终点的处理
        if (x == map.length-1 && y == map[0].length-1) {
            
            book[x][y] = steps; //保存状态序号
 
            int[][] ps = new int[steps][2];
            //获取路径坐标
            for (int i=0; i<book.length; i++) {
                for (int j=0; j<book[0].length; j++) {
                    if (book[i][j] != 0) {
                        ps[book[i][j]-1][0] = i;//根据状态序号决定顺序
                        ps[book[i][j]-1][1] = j;
                    }
                }
            }
            points = ps;

            book[x][y] = 0;
            return;
        }
        
        //当下状态的处理
        
        for (int i=0; i<4; i++) {//该状态下尝试4个方向
            book[x][y] = steps;//保存状态序号

            int toX = x+next[i][0];
            int toY = y+next[i][1];
            //边界判断,路径判断
            if (toX >= 0 && toX < map.length && toY >= 0 && toY < map[0].length) {
                if (map[toX][toY] == 0 && book[toX][toY] == 0) {
                    dfs(map, book, toX, toY, steps+1);
                }
            }

            book[x][y] = 0;//清除标记
        }
       
    }
 
    static void printPoints(int[][] a) {
        if (a == null) return;
        for (int i=0; i<a.length; i++) {
            System.out.printf("(%d,%d)\n",a[i][0],a[i][1]);
        }
    }

}

矩阵中的路径

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

其实就是dfs算法,类似找迷宫。

public class Solution {
    static public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
     {
         if (matrix == null || str == null || str.length == 0 || 
             matrix.length == 0 || matrix.length != rows*cols) return false;
             
         for (int i=0; i<rows; i++) {
             for (int j=0; j<cols; j++) {
                 //寻找起始位置
                 if (matrix[i*cols+j] == str[0]) {
                     isHas = false;
                     dfs(matrix, rows, cols, new int[rows*cols], str, 0, j, i);
                     if (isHas) 
                         return isHas;
                 }
             }
         }
         
         return false;
     }
     
     static boolean isHas = false; //找到字符串标志
     //book是用来标记一个位置是否被进入过
     //id 是遍历str字符串时的下标
     //x,y是对应二维矩阵中的坐标位置
     static void dfs(char[] matrix, int rows, int cols, 
                     int[] book, char[] str, int id, int x, int y) {
         //字符串被找到
         if (id == str.length) {
             isHas = true;
             return ;
         }
         
         //边界判断
         if (x < 0 || x >= cols || y < 0 || y >= rows) return;
         
         int index = x + y*cols;//对应matrix下标
         if (matrix[index] == str[id] && book[index] == 0) {
             book[index] = 1;//标志已经进入
             //进入下一批格子
             int[][] next = {{0,1},{1,0},{0,-1},{-1,0}};
             for (int i=0; i<4; i++) {
                 int tx = x + next[i][0];
                 int ty = y + next[i][1];
                 dfs(matrix, rows, cols, book, str, id+1, tx, ty);
             }
             
             book[index] = 0;//清除标志
         }
     }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容

  • 一、实验目的 学习使用 weka 中的常用分类器,完成数据分类任务。 二、实验内容 了解 weka 中 explo...
    yigoh阅读 8,463评论 5 4
  • 更新中。。。 第二章 2.2.2 交通规则 几种常见的渐近运行时间实例 2.2.4 三种重要情况 这里有一个 el...
    木一晟阅读 2,515评论 0 4
  • 每过一段时间都会感慨,都会怅然,回想起很多人,想起很多已遥远的事。 一路走来,许多人曾在身边相伴,互相扶...
    木薰阅读 382评论 0 2
  • 上个月幼儿园老师家访时,颜老师提醒我上了幼儿园后或多或少孩子都会出现一些状况,家长要淡定,这是个成长的过程,会出现...
    liuxinamy阅读 225评论 2 2
  • 无论是在简书阅读文章,还是在公众号亦或各大写作平台浏览,真心的很佩服那些能坚持日更的写作者。 他们是怀着怎样的一种...
    曼妙茶双子心董彣悦阅读 1,119评论 12 51