算法之深度优先搜索

简介: 算法之深度优先搜索

一、引言


研究算法,写算法是很枯燥的过程;但是找到规律以及通用之处,会发现它是如此简单,如此妙不可言,前提是需要有耐心。


本人不喜欢一上来就是扒拉扒拉一些概念然后弄个经典例题之后闪人。个人喜欢循序渐进,从小问题引出要用的算法;


深度优先搜索和广度优先搜索是搜索算法里面比较基础的算法,理解以及实现非常容易,当然说归说,如何容易,且看下文。


二、小小问题


Q:1~4 数字的全排;


第一种:一般情况看到这个题目,第一反应就是暴力穷举,4个for循环搞定一切;


第二种:采用深度优先搜索,但是第一反应,怎么可能啊?   第二反应,我不会啊,怎么深度优先搜索呢?很简单,让我们来写最简单的代码


第一种思路代码的V1版本:

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);
   }
}


是不是你想的那样? 简单吧? 让我们来优化一下这优雅的代码,让它更优雅点;


第一种思路代码优化V2:


Q: 基于V1版本的优化,如何优化呢?


想想,现在只是4个数字的全排,用4个循环解决,如果题目改一下,变成9个数字的全排,看下V1版本的代码要做如何的改动呢?


很简单,9个for,重复的内容,不变的循环。最后只需要更改下标,判断以及输出即可满足。如果是二十几个数字全排呢?崩溃了。


在这里,我引入递归。递归的特点,找出重复的计算以及边界条件即可,递归的概念这里不做任何讲解。具体的优化如下:

package dfs;
/**
* 暴力全排0~4
* V2版本:递归
* 边界点:多少个排序数
* 重复内容:for循环
*
* @author danqiusheng
*/
public class NormalFullV2 {
   static int[] arr = 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) { // 出口;当下标超出
           int[] result = new int[5];// 判断是否重复
           boolean flag = true;
           for (int i = 1; i < step_total; 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++;
           }
           return;
       }
       // 重复内容
       for (int i = 1; i < step_total; i++) {
           arr[step] = i; // 第step下标位置 放i
           full(step + 1);
       }
   }
}


看,是不是比for循环优雅一点;如果这时候需要二十几个数字全排,只需要将静态数组长度,step_total以及输出修改即可;    


但是问题又来了,这个代码我们能不能再优化一下呢?从刚才的暴力到现在的递归解决,会发现,判断条件那一块的代码是原封不动的照搬过来的;

int[] result = new int[5];
// 判断是否重复
boolean flag = true;
for (int i = 1; i < step_total; 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++;
}


这一块代码的主要意思是在全排完毕后判断数字是否重复了,如果重复就不输出;


那问题来了,我们能不能在之前就做好这个判断呢?比如在全排当前数的时候,我们能不能把当前全排的这个数与前面已经全排的数比较判断有没有重复呢?


第三种代码优化V3:

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;  
              // 标记当前数不全排,这一步是因为当前数已经全排完毕
              //(Full()这个方法递归已经结束!)
              //此时应该取消之前标记这个数的已经全排
          }
      }
  }
}



看,代码是不是少了很多呢?此时的V3版还可以怎么优化就看你们自己了,但是我今天的算法已经分享完了,V3版是深度优先搜索的模子,愣住了吗?V3是深度优先搜索? 深度优先搜索到底是什么呢?


三、概念

什么是深度优先搜索呢?深度优先搜索简称DFS,它最重要的两个点,第一个是当前该怎么做以及下一步该如何做。


正如我们的V3版Full方法里面的循环,当前这步放当前数,然后尝试下一步应该放什么。当然了,下一步的操作和当前这步的操作是一样的。 所以写起来很简单。


四、总结

深度优先搜索基于上面的概念,有一个基本的模版,如下:

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


当然,这只是一个基础的,因为不同的问题,深搜体现的方式不同。但是写了很多算法,大部分都是用for循环来进行尝试下一步要做什么。


五、小试牛刀


image.png


六、代码

目录
相关文章
|
算法 测试技术 定位技术
数据结构与算法——DFS(深度优先搜索)
数据结构与算法——DFS(深度优先搜索)
|
10月前
|
人工智能 运维 算法
基于 C# 深度优先搜索算法的局域网集中管理软件技术剖析
现代化办公环境中,局域网集中管理软件是保障企业网络高效运行、实现资源合理分配以及强化信息安全管控的核心工具。此类软件需应对复杂的网络拓扑结构、海量的设备信息及多样化的用户操作,而数据结构与算法正是支撑其强大功能的基石。本文将深入剖析深度优先搜索(Depth-First Search,DFS)算法,并结合 C# 语言特性,详细阐述其在局域网集中管理软件中的应用与实现。
237 3
|
11月前
|
监控 算法 安全
基于 PHP 语言深度优先搜索算法的局域网网络监控软件研究
在当下数字化时代,局域网作为企业与机构内部信息交互的核心载体,其稳定性与安全性备受关注。局域网网络监控软件随之兴起,成为保障网络正常运转的关键工具。此类软件的高效运行依托于多种数据结构与算法,本文将聚焦深度优先搜索(DFS)算法,探究其在局域网网络监控软件中的应用,并借助 PHP 语言代码示例予以详细阐释。
267 1
|
5月前
|
机器学习/深度学习 算法 机器人
【机器人路径规划】基于深度优先搜索(Depth-First-Search,DFS)算法的机器人路径规划(Python代码实现)
【机器人路径规划】基于深度优先搜索(Depth-First-Search,DFS)算法的机器人路径规划(Python代码实现)
319 3
|
12月前
|
运维 监控 JavaScript
内网网管软件中基于 Node.js 的深度优先搜索算法剖析
内网网管软件在企业网络中不可或缺,涵盖设备管理、流量监控和安全防护。本文基于Node.js实现深度优先搜索(DFS)算法,解析其在网络拓扑遍历中的应用。通过DFS,可高效获取内网设备连接关系,助力故障排查与网络规划。代码示例展示了图结构的构建及DFS的具体实现,为内网管理提供技术支持。
245 11
|
12月前
|
机器学习/深度学习 算法
算法系列之搜索算法-深度优先搜索DFS
深度优先搜索和广度优先搜索一样,都是对图进行搜索的算法,目的也都是从起点开始搜索,直到到达顶点。深度优先搜索会沿着一条路径不断的往下搜索,直到不能够在继续为止,然后在折返,开始搜索下一条候补路径。
905 62
算法系列之搜索算法-深度优先搜索DFS
|
11月前
|
算法 安全 Java
算法系列之深度优先搜索寻找妖怪和尚过河问题的所有方式
在算法学习中,深度优先搜索(DFS)是一种常用的图搜索算法,通过递归或栈实现,适合路径搜索、连通性、拓扑排序、回溯、生成、环路检测、强连通分量和可达性等问题。本文将介绍如何利用深度优先搜索解决“妖怪和尚过河问题”的所有方式。
230 26
算法系列之深度优先搜索寻找妖怪和尚过河问题的所有方式
|
11月前
|
存储 算法 JavaScript
基于 Node.js 深度优先搜索算法的上网监管软件研究
在数字化时代,网络环境呈现出高度的复杂性与动态性,上网监管软件在维护网络秩序与安全方面的重要性与日俱增。此类软件依托各类数据结构与算法,实现对网络活动的精准监测与高效管理。本文将深度聚焦于深度优先搜索(DFS)算法,并结合 Node.js 编程语言,深入剖析其在上网监管软件中的应用机制与效能。
163 6
|
11月前
|
监控 算法 JavaScript
企业用网络监控软件中的 Node.js 深度优先搜索算法剖析
在数字化办公盛行的当下,企业对网络监控的需求呈显著增长态势。企业级网络监控软件作为维护网络安全、提高办公效率的关键工具,其重要性不言而喻。此类软件需要高效处理复杂的网络拓扑结构与海量网络数据,而算法与数据结构则构成了其核心支撑。本文将深入剖析深度优先搜索(DFS)算法在企业级网络监控软件中的应用,并通过 Node.js 代码示例进行详细阐释。
222 2
|
12月前
|
存储 人工智能 算法
【深度优先搜索篇】走迷宫的魔法:算法如何破解迷宫的神秘密码
【深度优先搜索篇】走迷宫的魔法:算法如何破解迷宫的神秘密码