首页 > 其他分享 >dfs

dfs

时间:2024-05-10 09:02:27浏览次数:14  
标签:used nums int dfs candidates new path

1.1 排列组合问题

排列组合问题还得是卡子哥讲的通透

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

 

组合与排列

 组合问题是从给定的元素集合中选取若干元素,不考虑元素的顺序,求子集的问题;而排列问题是从给定的元素集合中选取若干元素,考虑元素的顺序,求不同排列的问题。

组合问题: 假设有集合 {A, B, C},我们想要从中选择两个元素,不考虑顺序。组合问题涉及选择的元素集合,如 {A, B}、{A, C}、{B, C},而不考虑元素的排列顺序。例如,{A, B} 和 {B, A} 视为相同的组合。

排列问题: 同样假设有集合 {A, B, C},我们想要从中选择两个元素,考虑元素的排列顺序。排列问题涉及选择的元素排列,如 (A, B)、(A, C)、(B, A)、(B, C) 等,每个排列视为不同的选择,因为元素的顺序不同。

 1.1.1 组合问题

力扣 216.组合总和III

思路:回溯

终止条件:sum == n && path.size == k

循环条件:每种组合中不存在重复的数字 => i +1 

处理节点:sum+=i ; path.add(i)

递归:backTracking(i+1,sum,k)

剪枝:因为是[1,2,3...9]的递增有序集和,所以剪枝操作放到for循环判断更有效。剪枝判断显然是 sum > n 

回溯:sum-=i; path.remove(path.size()-1);

public class Solution {
    /*
     * //使用static关键字声明res和path作为类的静态成员会导致在算法执行过程中出现问题,
     * // 特别是当涉及到多个测试用例或多个Solution类的实例时。
     * static List<List<Integer>> res = new ArrayList<>();
     * static List<Integer> path = new LinkedList<>();
     */
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new LinkedList<>();

    public List<List<Integer>> combinationSum3(int k, int n) {
        backTracking(k, n, 1, 0);
        return res;
    }

    private void backTracking(int k, int n, int startIndex, int sum) {
        if (sum > n)
            return;

        if (path.size() > k)
            return;

        if (sum == n && path.size() == k) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = startIndex; i <= 9; i++) {
            path.add(i);
            sum += i;
            backTracking(k, n, i + 1, sum);
            sum -= i;
            path.remove(path.size() - 1);
        }
    }

}

组合总和II

与上一题区别在于:

  1. 本题数组candidates的元素是有重复的,而上一题是无重复元素的数组candidates

最后本题和上一题要求一样,解集不能包含重复的组合。上一题因为没有重复元素,只需要保证startIndex = i+1就不会出现重复组合

但是这题的 集合(数组candidates)有重复元素,但还不能有重复的组合。

对于这道问题,由于数组 candidates 中的元素可能重复,但解集中不能包含重复的组合,我们可以采用以下策略:

  1. 排序数组:首先对数组 candidates 进行排序,这样相同的元素会相邻排列。

  2. 回溯法避免重复:在回溯过程中,为了避免产生重复的组合,我们可以在每一层递归中跳过相同的元素,以确保不会重复选择相同的组合。这步操作对应卡哥说的树层去重操作.

 

class Solution {
  List<List<Integer>> res = new ArrayList<>();
  LinkedList<Integer> path = new LinkedList<>();
  int sum = 0;
  
  public List<List<Integer>> combinationSum2( int[] candidates, int target ) {
    //为了将重复的数字都放到一起,所以先进行排序
    Arrays.sort( candidates );
    backTracking( candidates, target, 0 );
    return res;
  }
  
  private void backTracking( int[] candidates, int target, int start ) {
    if ( sum == target ) {
      res.add( new ArrayList<>( path ) );
      return;
    }
    for ( int i = start; i < candidates.length && sum + candidates[i] <= target; i++ ) {
      //正确剔除重复解的办法
      //跳过同一树层使用过的元素
      if ( i > start && candidates[i] == candidates[i - 1] ) {
        continue;
      }

      sum += candidates[i];
      path.add( candidates[i] );
      // i+1 代表当前组内元素只选取一次
      backTracking( candidates, target, i + 1 );

      int temp = path.getLast();
      sum -= temp;
      path.removeLast();
    }
  }
}

  

1.1.2 排列问题

 

47.全排列 II

排列问题去重

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    boolean[] used;
    public List<List<Integer>> permuteUnique(int[] nums) {
        used = new boolean[nums.length];
        Arrays.fill(used,false);
        Arrays.sort(nums);
        backTracking(nums);
        return res;
    }
    public void backTracking(int[] nums){
        if(path.size() == nums.length){
            res.add(new ArrayList<>(path));
            return;
        }
        for(int i = 0 ; i < nums.length ; i++){
            if(i > 0 && nums[i-1] == nums[i] && used[i-1] == false){
                continue;
            }
            if(used[i] == false){
                used[i] = true;
                path.add(nums[i]);
                backTracking(nums);
                used[i] = false;
                path.removeLast();
            }
        }

    }
}

LeetCode 996. 正方形数组的数目

题解:全排列+排序去重+相邻元素特判  
package com.coedes.dfs.likou996;


import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

/**
 * @description:https://leetcode.cn/problems/number-of-squareful-arrays/
 * @author: wenLiu
 * @create: 2024/5/9 23:23
 */
public class Solution {

    int cnt = 0;

    public int numSquarefulPerms(int[] nums) {

        int n = nums.length;
        Arrays.sort(nums);
        boolean[] used = new boolean[n + 1];
        Arrays.fill(used, false);
        List<Integer> path = new LinkedList<>();
        dfs(0,nums, used, path);
        return cnt;
    }

    private void dfs(int i, int[] nums, boolean[] used, List<Integer> path) {
        if (i == nums.length) {
            cnt++;
            return;
        }
        for (int j = 0; j < nums.length; j++) {
            if (used[j]) continue;
            // 去重
            if (j > 0 && nums[j - 1] == nums[j] && !used[j-1]) continue;
            // 相邻元素和是否为完全平方数判断
            if (path.size() > 0 && !check(path.get(path.size() - 1) + nums[j])) continue;
            used[j] = true;
            path.add(nums[j]);
            dfs(i+1, nums, used, path);
            used[j] = false;
            path.remove(path.size() - 1);
        }
    }

    private boolean check(int i) {
        int sqrOfi = (int) Math.sqrt(i);
        return sqrOfi * sqrOfi == i;
    }
}

  

 

acwing 94. 递归实现排列型枚举

题解:通过dfs方式求排列,本质还是回溯

https://www.acwing.com/solution/content/44647/
import java.util.Scanner;

public class Main {
    static final int N = 10;
    static int[] nums = new int[N];  // 存储排列的数组
    static boolean[] st = new boolean[N];  // 标记数组,表示每个数字是否已经被使用过
    static int n;  // 排列的长度

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        dfs(1);  // 从第一个位置开始深度优先搜索
    }

    static void dfs(int u) {
        // 如果已经排列完所有的数字,输出结果
        if (u > n) {
            for (int i = 1; i <= n; i++)
                System.out.print(nums[i] + " ");
            System.out.println();
            return;
        }
        // 枚举可选的数字
        for (int i = 1; i <= n; i++) {
            // 如果数字未被使用过,选择它并继续搜索
            if (!st[i]) {
                nums[u] = i;  // 将数字 i 放入当前位置
                st[i] = true;  // 标记数字 i 已被使用
                dfs(u + 1);  // 继续搜索下一个位置
                st[i] = false;  // 恢复状态,回溯
            }
        }
    }
}

  

2023柠檬微趣-排队

题目描述:

  1. 问题描述:给定了 m 位学生,每位学生有唯一的编号 1m。现在需要找出所有学生排队的方案,并按照字典序升序排序。

  2. 问题求解:需要找出所有可能的学生排队方案,这些方案按照字典序排序,然后输出第 n 个排队方案。

输入描述

输入第一行为一个整数 m ,表示学生和位置数。(1≤m≤10 )

输入第二行为一个整数 n ,表示排列方案序号。( 1≤n≤10^9 )

输出描述

若存在第 n 个方案,输出作为方案,数组间元素用空格隔开。

若不存在该方案,输出 −1。

eg1:
input:
4
5

output:
1 4 2 3

eg2:
input:
3
7

output:
-1

 思路:m个数全排列+计数

  题目可以转化为 : 打印m个数字的全排列中的第n个排列

 这道题用卡哥

static List<List<Integer>> res = new ArrayList<>();

static List<Integer> path = new LinkedList<>();
收集答案会爆内存...
以后尽量用数组收集吧...

package com.coedes.dfs.ningmeng2023050604;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;



/**
 * @description:https://codefun2000.com/p/P1280
 * @author: wenLiu
 * @create: 2024/5/9 10:16
 */
public class Main {
    static int cnt = 0;
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int m = Integer.parseInt(reader.readLine());
        int n = Integer.parseInt(reader.readLine());

        int[] path = new int[m];

        boolean[] used = new boolean[m+1];
        Arrays.fill(used,false);
    
        int[] f = new int[m + 1];
        f[1] = 1;
        for (int i = 2; i <= m; i++) {
            f[i] = f[i - 1] * i;
        }
     //m个数全排列最多有m!种 
        if (f[m] < n) {
            System.out.println(-1);
        }else {
            dfs(0,m,n,path,used);
        }
    }

    private static void dfs(int i, int m, int n, int[] path, boolean[] used) {
        if (i == m) {
            cnt++;
            if (cnt==n) {
                for (int i1 : path) {
                    System.out.print(i1+" ");
                }
            }
            return;
        }
        for (int j = 1; j <= m; j++) {
            if (used[j]== true) {
                continue;
            }
            used[j] = true;
            path[i] = j;
            dfs(i+1,m,n,path,used);
            used[j] = false;
        }
    }
}

  

 1.2 子集方式枚举

 

 

 

标签:used,nums,int,dfs,candidates,new,path
From: https://www.cnblogs.com/alohablogs/p/18152907

相关文章

  • DFS序
    DFS序题目链接:DFS序2DFS序其实是利用时间戳按照DFS访问顺序对每个节点标号in[p]:以p(节点编号)为根节点的子树的开头out[p]:以p(节点编号)为根节点的子树的结尾​ 例:in[1]=4,out[1]=8,代表以节点编号1为根节点的子树,DFS序对应时间戳为[4,8]dfs_order[]:存......
  • centos安装fastdfs
    安装前的准备检查Linux上是否安装了gcc、libevent、libevent-devel点击查看代码yumlistinstalled|grepgccyumlistinstalled|greplibeventyumlistinstalled|greplibevent-devel————————————————​如果没有安装,则需进行安装点击查看......
  • CF-877-E-dfs序+线段树
    877-E题目大意给定一颗\(n\)个节点的树,根为\(1\),点带权,权值要么为0,要么为1。\(q\)次询问,两种类型:\(get\spacex\):询问\(x\)的子树中有多少个\(1\)。\(pow\spacex\):将\(x\)子树中所有的值取反。Solutiondfs序+线段树模板题,把子树上的操作转化为区间上的操作。时间......
  • Codeforces 1044F DFS
    考虑到存在方案使得以\(u\)为起点还能走出原树的条件。能发现是以\(u\)为根,在原树上新添加的边只会是返祖边而不会有横叉边。这是因为如果有横叉边就肯定会在遍历到一边的点后先通过这条边走到另一边的点,就算上了这条边就不是原树了。那么考虑\((x,y)\),合法的\(u\)需要......
  • 通配符匹配|dfs,hash|题解
    [CQOI2014]通配符匹配题目描述几乎所有操作系统的命令行界面(CLI)中都支持文件名的通配符匹配以方便用户。最常见的通配符有两个,一个是星号(*),可以匹配0个及以上的任意字符:另一个是问号(?),可以匹配恰好一个任意字符。现在需要你编写一个程序,对于给定的文件名列表和一个包含通配符的......
  • 24/04/27 图论及 dfs 序相关
    \(\color{green}(1)\)CF721CJourney给定一张\(n\)个点\(m\)条边的有向无环图,边有边权。构造一条\(1\ton\)的路径使得其边权和\(\lek\)且经过的点数最多。\(n,m\le5\times10^3\),\(k\le10^9\)。最简单的想法是设状态\(f_{i,j}\)表示\(1\toi\)的边权......
  • 蓝桥杯-数的划分(DFS)
    0.题目问题描述将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。例如:n=7,k=3,下面三种分法被认为是相同的。1,1,5;1,5,1;5,1,1;问有多少种不同的分法。输入格式n,k输出格式一个整数,即不同的分法样例输入73样例输出4数据......
  • 深度优先搜索 Depth First Search (DFS)
    本篇篇幅较长,请做好心理准备!共三章节:1.深搜入门(一维方向数字选数类)2.深搜入门(二维方向迷宫类)3.深搜进阶(迷宫类问题--最少步数和输出路径)第一章:深搜入门(一维方向数字选数类)前置知识:函数、递归为了保证学习效果,请保证已经掌握前置知识之后,再来学习本章节!深度优......
  • 利用 Amazon EMR Serverless、Amazon Athena、Apache Dolphinscheduler 以及本地 TiDB
    引言在数据驱动的世界中,企业正在寻求可靠且高性能的解决方案来管理其不断增长的数据需求。本系列博客从一个重视数据安全和合规性的B2C金融科技客户的角度来讨论云上云下混合部署的情况下如何利用亚马逊云科技云原生服务、开源社区产品以及第三方工具构建无服务器数据仓库的解......
  • 数据结构与算法学习(1)——DFS(深度优先搜索)
    DFS基础深度优先搜索算法(英语:Depth-First-Search,缩写为DFS)是一种用于遍历或搜索树或图的算法。这个算法会尽可能深地搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发......