每日一算法:深度优先算法

news/2024/7/20 20:29:40 标签: 算法, 深度优先

深度优先搜索(Depth-First Search,简称DFS)是一种用于遍历或搜索树或图的算法。如其名,该算法深入到可能的分支上,直到目标节点被找到或者已经没有其他节点可以继续深入,此时算法回溯到上一节点,以探索未被遍历的路径。深度优先搜索是一个递归算法,它利用了后进先出的栈结构,在图的遍历中特别有效。

深度优先搜索的步骤:

  1. 选择起点:从图中的某个顶点开始遍历。
  2. 访问节点:访问当前节点。如果该节点是目标节点,则搜索成功,返回结果。
  3. 递归遍历:对当前节点的所有未访问的相邻节点,采用深度优先搜索的方式递归遍历。
  4. 回溯:当当前节点的所有相邻节点都被访问过,或者在相邻节点中没有找到目标节点,算法回溯到上一节点继续执行步骤3。
  5. 结束条件:当所有节点都被访问过,或者找到目标节点,算法结束。

深度优先搜索的特点:

  • 内存消耗较小:由于只需存储一条从根节点到当前节点的路径,因此相较于宽度优先搜索,其内存消耗通常较小。
  • 找到解后停止:在搜索到解后,可以立即停止,不需要遍历整个图。
  • 可能陷入死循环:在遍历图时,如果不记录已经访问过的节点,可能会陷入无限循环的情况。
  • 不保证最短路径深度优先搜索不保证找到的路径是最短的,它只是尽可能深地搜索图。

深度优先搜索的应用:

  • 解决迷宫问题:可以用来寻找从起点到终点的路径。
  • 排列组合问题:可以用来生成所有可能的排列组合。
  • 拓扑排序:在有向无环图中,深度优先搜索可以用来进行拓扑排序。
  • 解决连通性问题:可以用来判断两个节点是否连通,或者整个图的连通分量。

深度优先搜索的伪代码:

DFS(node):
  if node is visited:
    return

  mark node as visited
  for each node_neighbor in node.adjacent_nodes:
    if node_neighbor is not visited:
      DFS(node_neighbor)

在实际编程中,深度优先搜索可以通过递归实现,也可以通过显式地使用栈来实现非递归版本。递归版本的实现较为简洁,但在遇到深度非常大的图时可能会导致栈溢出。非递归版本虽然代码复杂一些,但可以避免栈溢出的问题。

案例

1.全排列
问题描述:

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

代码实现:
public static List<List<Integer>> res = new LinkedList<>();

    /**
     * 输入一组不重复的数字,返回它们的全排列
     *
     * @param nums
     * @return
     */
    public static List<List<Integer>> permute(int[] nums) {
        // 记录「路径」
        LinkedList<Integer> track = new LinkedList<>();
        backtrack(nums, track);
        return res;
    }

    public static void backtrack(int[] nums, LinkedList<Integer> track) {
        if (track.size() == nums.length) {
            res.add(new LinkedList<>(track));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            int num = nums[i];
            if (track.contains(num)) {
                continue;
            }
            track.add(num);
            backtrack(nums, track);
            track.removeLast();
        }
    }
2.N皇后问题
问题描述:

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

代码实现:
public static List<String[][]> result = new LinkedList<>();

    /**
     * N皇后问题
     * 如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
     * 给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
     *
     * @param n 皇后个数
     * @return 满足条件的所有方式
     */
    public static List<String[][]> solveNQueens(int n) {


        String[][] strings = new String[n][n];

        Arrays.fill(strings, ".");

        backtrack(strings, 0);

        return result;
    }

    public static void backtrack(String[][] strings, int i) {

        if (i >= strings.length) {
            result.add(strings);
            return;
        }

        for (int j = 0; j < strings.length; j++) {
            if (!isValid(strings, i, j)) {
                continue;
            }
            strings[i][j] = "Q";
            backtrack(strings, i + 1);
            strings[i][j] = ".";
        }

    }

    /**
     * i行j列放置皇后是否满足要求
     * @param strings 棋盘布局
     * @param i 行
     * @param j 列
     * @return 是否满足要求
     */
    public static boolean isValid(String[][] strings, int i, int j) {
        //判断同一行是否已经有
        for (int k = 0; k < i; k++) {
            if ("Q".equals(strings[i][k])) {
                return false;
            }
        }
        //判断左上方是否有
        for (int m = i - 1, n = j - 1; m >= 0 && n >= 0; m--, n--) {
            if ("Q".equals(strings[m][n])) {
                return false;
            }
        }
        //判断右上方是否有
        for (int m = i - 1, n = j + 1; m >= 0 && n <= j; m--, n++) {
            if ("Q".equals(strings[m][n])) {
                return false;
            }
        }
        return true;
    }
3.子集问题
问题描述:

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

代码实现:
public static List<List<Integer>> res2 = new LinkedList<>();

    /**
     * 子集问题
     * 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
     * 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
     *
     * @param nums 数组
     * @return 所有可能的子集
     */
    public static List<List<Integer>> subsets(int[] nums) {
        LinkedList<Integer> track = new LinkedList<>();
        backtrack(nums, 0, track);
        return res2;
    }

    public static void backtrack(int[] nums, int start, LinkedList<Integer> track) {

        res2.add(new LinkedList<>(track));

        for (int i = start; i < nums.length; i++) {
            track.add(nums[i]);
            backtrack(nums, i + 1, track);
            track.removeLast();
        }

    }
4.组合问题
问题描述:

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合

代码实现:
public static List<List<Integer>> res3 = new LinkedList<>();

    /**
     * 组合问题
     * 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
     * @param n
     * @param k
     * @return 所有可能的组合
     */
    public static List<List<Integer>> combine(int n, int k) {
        LinkedList<Integer> track = new LinkedList<>();
        backtrack(n, 1, k, track);
        return res3;
    }

    public static void backtrack(int n, int start, int k, LinkedList<Integer> track) {

        if (track.size() == k) {
            res3.add(new LinkedList<>(track));
            return;
        }
        for (int i = start; i <= n; i++) {
            track.add(i);
            backtrack(n, i + 1, k, track);
            track.removeLast();
        }

    }

http://www.niftyadmin.cn/n/5262004.html

相关文章

GPIO的使用--滴答定时器--pir人体红外传感器

目录 一、滴答定时器的使用与原理 1、定义 2、原理 &#xff08;1&#xff09;向上计数​编辑 &#xff08;2&#xff09;向下计数 &#xff08;3&#xff09; 代码流程 a、配置滴答时钟唤醒频率 b、滴答时钟中断函数 &#xff08;4&#xff09;结果 3、优化-->寄存…

磁盘空间分析工具你知道几个!8个易学易用的磁盘分析工具,让你对硬盘知根知底

以下是我对可用磁盘空间分析工具(有时称为存储分析器)的首选列表。在我的电脑上试用了其中几个应用程序后,我可以确认这里列出的应用程序是100%免费使用的,并且在弄清楚是什么填满了硬盘、闪存驱动器或外部驱动器中做了很多工作,其中一些甚至允许你直接从程序中删除文件。…

第14节:Vue3 简化用法<script setup>

下面是一个示例&#xff0c;演示了如何在UniApp中使用 <template> <view> <text>{{ message }}</text> <button click"changeMessage">点击改变文本</button> </view> </template> <script setup> imp…

H5开发App应用程序的常见问题以及解决方案

Hello大家好&#xff0c;我是咕噜铁蛋&#xff0c;天冷记得添衣&#xff0c;ok话说回来H5开发成为了一种流行的方式来构建跨平台的移动应用程序。然而&#xff0c;在H5开发App应用程序的过程中&#xff0c;我们常常会遇到一些问题&#xff0c;这些问题可能涉及性能、兼容性、用…

【字符串】ABC324E

退役啦&#xff0c;接下来的博客全是图一乐啦 E - Joint Two Strings 题意 思路 统计两个指针的方案数一定是枚举一个&#xff0c;统计另一个 然后因为拼起来之后要包含 t 这个字符串&#xff0c;隐隐约约会感觉到和前缀后缀子序列有关 考虑预处理每个 s[i] 的最长公共前…

Vue3:表格单元格内容由:图标+具体内容 构成

一、背景 在Vue3项目中&#xff0c;想让单元格的内容是由 &#xff1a;图标具体内容组成的&#xff0c;类似以下效果&#xff1a; 二、图标 Element-Plus 可以在Element-Plus里面找是否有符合需求的图标iconfont 如果Element-Plus里面没有符合需求的&#xff0c;也可以在这…

Codeforces Round 774 (Div. 2) (D树形dp上司的舞会 C二进制枚举+快速幂? E打表求每个底数不同贡献)

A - Square Counting 直接能填就填n*n就填&#xff0c;不然全0啥的即可 #include<bits/stdc.h> using namespace std; const int N 3e510,mod998244353; #define int long long typedef long long LL; typedef pair<int, int> PII; typedef unsigned long long …

2-2基础算法-递归/进制转换

文章目录 一.递归二.进制转换 一.递归 1.数的计算 评测系统 #include <iostream> int countCombinations(int n) { //计算当然组合种数if (n 1) {return 1;}int count 1;//数字本身就是一个有效组合for (int i 1; i < n / 2; i) {count countCombinations(i);/…