深度优先搜索算法 - Go语言中文社区

深度优先搜索算法


    本文主要部分来源于微信公众号:算法文摘,自己感悟和习题的练习。(侵删)

    1. 深度优先搜索算法的概念:

        深度优先搜索属于图算法的一种,英文缩写为DFS(Depth First Search.)其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。如下例:

        

        该图为一个无向图,假设我们从A开始进行深度优先搜索,第二点可以是B、C、D中任意一个,假设访问的是B,那么路径为A - B - E,搜索完成后直接退回B,搜索其下一个子节点,完成B之后退到A,搜索C节点,以此类推,直到找出需要的解。

        DFS适合此类题目:给定初始状态跟目标状态,要求判断从初始状态到目标状态是否有解。

        深度优先搜索(DFS)有两个重要的点:第一是当前怎么做,第二是下一步怎么做;模板如下:

public static void dfs (int step) {
        //判断边界,递归出口
        //遍历每一种可能,经进行递归
        for (int i = 1; i < n; i++) {
            dfs(step + 1);
        }
    }

        文字描述的内容比较简单,网上搜索一大堆,下面结合具体的实例来理解下深度优先搜索算法的精髓。

2.  实例:

        例题1:将1-4,几个数字进行全排列,列出所有的可能和个数。

        解法一:最暴力的解法就是进行4重循环,然后判断各个元素是否重复,不重复就是其中一个解;如:1111,1112,1113....一直列到4444,取出所有不重复的解就是答案。代码如下:

        

package dfs;

/**
 * 暴力全排0~4
 * 思路:四个循环,从1开始到4结束.在这里优化了一下,用数组存储,然后用另一个数组存储结果,之后相加个数就可以知道有没有数字重复。
 * 因为对于全排,只有1,2,3,4仅且出现1次。
 *
 * @author danqiusheng
 */
public class NormalFullV1 {

    public static void main(String[] args) {
        int count = 0;// 记录排序次数
        int[] arr = new int[5]; // 存储结果
        for (arr[1] = 1; arr[1] < 5; arr[1]++) {
            for (arr[2] = 1; arr[2] < 5; arr[2]++) {
                for (arr[3] = 1; arr[3] < 5; arr[3]++) {
                    for (arr[4] = 1; arr[4] < 5; arr[4]++) {
                        int[] result = new int[5];// 判断是否重复
                        boolean flag = true;
                        for (int i = 1; i < 5; i++) {
                            result[arr[i]] += 1;
                            if(result[arr[i]] > 1) flag = false; // 判断当前数字是否重复出现
                        }
                        if(flag){ // 所有的数字出现
                            System.out.println(arr[1] + "" + arr[2] + "" + arr[3] + "" + arr[4]);
                            count++;
                        }
                    }
                }
            }
        }
        System.out.println("total count:"+count);
    }
}

    这段代码中关键的难点在于判断是否重复,其应用到的是数组。新建一个判断重复的数组result[],其默认的数组数都是0,将arr[i]作为其下标,若arr[i]相同,则result[]的数组数是同一个,将其进行自增,若存在重复的数即arr[i]肯定有相同的,那么对result[]的其中一个数组数肯定大于1,从而进行重复的判断;比如1123,其中1,1是重复的,即arr[1] 和 arr[2] 都是1,将其带到result[arr[i]中,result[1]自增了两次成为了2,大于1,存在重复。(注意代码中的数组都是从1开始循环的而不是0)

        解法二,应用到深度优先搜索算法,先上代码:

package dfs;

/**
 * 暴力全排0~4
 * V3版本:递归时判断当前数是否重复
 * 边界点:多少个排序数
 * 重复内容:for循环
 *
 * @author danqiusheng
 */
public class NormalFullV3 {
    static int[] arr = new int[5]; // 存储结果数组
    static int[] result = new int[5];// 判断是否重复
    static int step_total = 5;
    static int count = 0;

    public static void main(String[] args) {
        full(1); // 1开始
        System.out.println("total count:" + count);
    }

    public static void full(int step) {
        if (step >= step_total) { // 出口;当下标超出
            System.out.println(arr[1] + "" + arr[2] + "" + arr[3] + "" + arr[4]);
            count++;
            return;
        }
        // 重复内容
        for (int i = 1; i < step_total; i++) {
            if (result[i] == 0) { // 如果当前数没有被全排
                result[i] = 1; // 标记当前数已经全排
                arr[step] = i; // 第step下标位置 放i
                full(step + 1);
                result[i] = 0;  // 标记当前数不全排
            }
        }
    }
}

       代码最关键的部分是,重复部分的循环。将循环过的result[i]设置为1,来避免重复循环,即当arr[1] = 1时,arr[2]在循环时i = 1时不进行任何操作,继续进行循环,当i = 2时,进行操作,并把2设置为不可用状态,然后进行递归调用,直到满足需求去执行full的第一个if。结合前面的概念部分的图表,第一层进入后依次进入后面的几层,而不重复进入第一层(数字1,不会重复出现),直到把一条路径上的走到尽头,然后回到上一个节点,走另外一条路到尽头,知道遍历完所有的节点。(递归调用的方式,这些步骤会被自动的实现)。

       例题二:从V0出发,搜索出一条长度为4的路径;


/**
 * DFS核心伪代码
 * 前置条件是visit数组全部设置成false
 * @param n 当前开始搜索的节点
 * @param d 当前到达的深度,也即是路径长度
 * @return 是否有解
 */
bool DFS(Node n, int d){
	if (d == 4){//路径长度为返回true,表示此次搜索有解
		return true;
	}

	for (Node nextNode in n){//遍历跟节点n相邻的节点nextNode,
		if (!visit[nextNode]){//未访问过的节点才能继续搜索

			//例如搜索到V1了,那么V1要设置成已访问
			visit[nextNode] = true;

			//接下来要从V1开始继续访问了,路径长度当然要加

			if (DFS(nextNode, d+1)){//如果搜索出有解
				//例如到了V6,找到解了,你必须一层一层递归的告诉上层已经找到解
				return true;
			}

			//重新设置成未访问,因为它有可能出现在下一次搜索的别的路径中
			visit[nextNode] = false;

		}
		//到这里,发现本次搜索还没找到解,那就要从当前节点的下一个节点开始搜索。
	}
	return false;//本次搜索无解
}

     练习:

        一.  ABC+DEF=GHI,数字1~9,不重复填入;

        二. A + B/C + DEF/GHI = 10,条件同上;

            (可以先自己完成,然后对照我做的)


参考答案:

        

package com.ncu.demo.test;

/**
 * 1-9任意一个数字实现 ABC + DEF = GHI; 列出实现的式子和个数
 */
public class DfsTest1 {

    static int [] arr = new int[10];
    static int [] result = new int[10];
    static int step_total = 10;
    static int count = 0;

    public static void dfs1 (int step) {

        //具体进行执行判断和输出
        if (step >= step_total) {
            int ABC = Integer.parseInt("" + arr[1] + arr[2] + arr[3]);
            int DEF = Integer.parseInt("" + arr[4] + arr[5] + arr[6]);
            int GHI = Integer.parseInt("" + arr[7] + arr[8] + arr[9]);
            if (ABC + DEF == GHI) {
                System.out.println(arr[1] + "" + arr[2] + "" + arr[3] + "+"
                        + arr[4] + "" + arr[5] + "" + arr[6] + "=" + arr[7] + "" + arr[8] + "" +arr[9]);
                count ++;
                return;
            }
        }
        //重复且不相等的生成arr[]
        for (int i = 1; i < step_total; i++) {
            if (result[i] == 0) {
                result[i] = 1;
                arr[step] = i;
                dfs1(step + 1);
                result[i] = 0;
            }
        }
    }

    public static void main(String[] args) {
        dfs1(1);
        System.out.println("Total count : " + count);
    }
}
package com.ncu.demo.test;

/**
 * A-I为1-9的代表,列出能实现 A + B/C + DEF/GHI = 10,的式子和个数
 */
public class DfsTest2 {

    static int [] arr = new int[10];
    static int [] result = new int[10];
    static int total_step = 10;
    static int count = 0;

    public static void dfs2 (int step) {

        if (step >= total_step) {
            float A = arr[1];
            float B = arr[2]/arr[3];
            float DEF = Float.parseFloat("" + arr[4] + arr[5] + arr[6]);
            float GHI = Float.parseFloat("" + arr[7] + arr[8] + arr[9]);
            if (A + B + DEF/GHI == 10) {
                count++;
                System.out.println(arr[1] + "+" + arr[2] + "/" + arr[3] + "+" + arr[4] + arr[5] + arr[6] + "/" +
                        arr[7] + arr[8] + arr[9] + "=" + 10 );
            }
            return;
        }
        //遍历实现1-9的赋值,不重复的最快速度
        for (int i = 1; i < total_step; i++) {
            if (result[i] == 0) {
                result[i] = 1;
                arr[step] = i;
                dfs2(step + 1);
                result[i] = 0;
            }
        }
    }

    public static void main(String[] args) {
        dfs2(1);
        System.out.println("Total count : " + count);
    }
}



版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/victoryshen/article/details/80227222
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-05-29 18:03:46
  • 阅读 ( 1449 )
  • 分类:算法

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢