dfs_全排列

1.  输入一个字符串,输出该字符串的全部全排列集合

2. 我们这里使用dfs深度优先搜索算法来解决,每一次都判断当前的索引对应的字符是否在原来使用过,若使用过则不进入循环中的dfs,继续扫描下一个,若之前还没有使用这个字符,那么把这个字符加进来,这里需要注意的是,在把字符加进来之前需要把原来的字符串临时保存一下,因为当这一层的递归调用结束之后原来的字符串已经增加内容改变了,所以需要在退回到这一层的时候需要进行回溯一下,把字符串恢复到原来的状态,防止它对进入循环的下一个dfs状态的影响

3. 具体的代码如下:

import java.util.Scanner;
public class Main{
    static int count = 0;
    static int n;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        char arr[] = sc.next().toCharArray();
        n = arr.length;
        dfs("", arr, 0);
        System.out.println(count);
    }
    
    private static void dfs(String srcStr, char arr[], int cur){
        if(cur == n){
            count++;
            System.out.println(srcStr);
            return;
        }
        
        for(int i = 0; i < n; i++){
            int index = check(srcStr, arr, i);
            if(index != -1){
                //这里要进行回溯,因为当这层的dfs结束之后srcStr已经改变了,所以在调用之前先要保存一下
                //原来的srcStr,这样在后面在继续往字符串增加字符的时候才不会出错,假如不回溯的话输入ABC的话只会输出一个ABC
                String temp = srcStr;
                srcStr += arr[i];
                dfs(srcStr, arr, cur + 1);
                srcStr = temp;
            }
        }
    }

    private static int check(String srcStr, char[] arr, int k){
        for(int i = 0; i < srcStr.length(); i++){
            if(srcStr.charAt(i) == arr[k]){
                return -1;
            }
        }
        return 0;
    }
}

这里以简单的例子ABC为例,假如这里不进行回溯的话那么在调用完使用输出语句看一下原来的字符串的情况,会发现srcStr会多出原来上一次增加的字符那么最后就会少调用一次,导致调用时候cur最大值为2永远到不了dfs的出口,控制台就会只输出一个ABC

这里我们可以在dfs调用结束之后输出原来字符串的情况,并且可以在dfs的出口输出cur来观察调用情况,调试的代码如下,可以观察控制台的输出结果来进行理解是否需要进行回溯

import java.util.Scanner;
public class Main{
    static int count = 0;
    static int n;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        char arr[] = sc.next().toCharArray();
        n = arr.length;
        dfs("", arr, 0);
        System.out.println(count);
        sc.close();
    }
    
    private static void dfs(String srcStr, char arr[], int cur){
        System.out.println(cur);
        if(cur == n){
            count++;
//            System.out.println(srcStr);
            return;
        }
        
        for(int i = 0; i < n; i++){
            int index = check(srcStr, arr, i);
            if(index != -1){
                //少了回溯的话原来就已经存在了AB那么就会少调用一次那么cur = 2不会进入到递归出口那里
                //那么就会只输出一个ABC
                //String temp = srcStr;
                srcStr += arr[i];
                dfs(srcStr, arr, cur + 1);
                System.out.println("cur " + cur);
                System.out.println(srcStr);
                //srcStr = temp;
            }
        }
    }

    private static int check(String srcStr, char[] arr, int k){
        for(int i = 0; i < srcStr.length(); i++){
            if(srcStr.charAt(i) == arr[k]){
                return -1;
            }
        }
        return 0;
    }
}
 

输出结果:

2
3
cur 2
ABC
cur 1
AB
2
cur 1
ABC
cur 0
A
1
2
cur 1
ABC
cur 0
AB
1
cur 0
ABC

每一次dfs调用返回之后才会执行dfs后面的代码,即当 dfs(srcStr, arr, cur + 1);返回之后才会执行这句代码之后的代码,当执行dfs(srcStr, arr, 3); 的时候那么碰到递归出口退回来但是不会执行dfs(srcStr, arr, 3); 这句代码下面的代码,而是退到上一层继续执行上一层dfs(srcStr, arr, 2);下面的代码

猜你喜欢

转载自blog.csdn.net/qq_39445165/article/details/83472670
今日推荐