剑指 offer:DFS字典序,全排列问题,递归调用树

题目描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

链接:https://www.nowcoder.com/questionTerminal/fe6b651b66ae47d7acce78ffdd9a96c7
来源:牛客网

import java.util.*;
 
public class Solution {
    private char [] seqs;
    private Integer [] book;
    //用于结果去重
    private HashSet<String> result = new HashSet<String>();
    /**
     * 输入一个字符串,按字典序打印出该字符串中字符的所有排列。
     * 例如输入字符串abc,则打印出由字符a,b,c
     * 所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 结果请按字母顺序输出。
       输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。\
     * @param str
     * @return
     */
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> arrange = new ArrayList<String>();
        if(str == null || str.isEmpty()) return arrange;
        char[] strs = str.toCharArray();
        seqs = new char[strs.length];
        book = new Integer[strs.length];
        for (int i = 0; i < book.length; i++) {
            book[i] = 0;
        }
        dfs(strs, 0);
        arrange.addAll(result);
        Collections.sort(arrange);
        return arrange;
    }
 
    /**
     * 深度遍历法
     */
    private void dfs(char[] arrs, int step){
        //走完所有可能 记录排列
        if(arrs.length == step){
            String str = "";
            for (int i = 0; i < seqs.length; i++) {
                str += seqs[i];
            }
            result.add(str);
            return; //返回上一步
        }
        //遍历整个序列,尝试每一种可能
        for (int i = 0; i < arrs.length; i++) {
            //是否走过
            if(book[i] == 0){
                seqs[step] = arrs[i];
                book[i] = 1;
                //下一步
                dfs(arrs, step + 1);
                //走完最后一步 后退一步
                book[i] = 0;
            }
        }
    }
}

代码思想

a->b->c

回退一步,step--,step == 2,到c,收回c,book[c] = 0;

回退一步,step--,step == 1,到b,  收回b, book[b] = 0,此时执行for循环到i = 2,step = 1,刚好把刚刚收回的c放进seqs[1]中

,book[c] = 1.然后进入dfs到c,将刚刚收回的b放进seqs[2]中,输出字符串。

回退一步,step--,step == 2,到c,收回c,book[c] = 0;

回退到原始的a的位置,将b放入a的位置(0号位),将a收回。进入下一个dfs,将a放入1号位,又因为book[c] = 1,所以直接输出字符串。

....

最后输出结果:

abc

acb

bac

bca

cab

cba

简单理解,地上有三个并排的位置,标记为0,1,2。一个人手里有三副扑克牌,标记为A,B,C。他从0开始往2移动,一次移动一个位置。

到0时把扑克A放进去,到1时把扑克B放进去,到2时把扑克C放进去。此时step= 2,再向后移一个位置,step = 3,此时输出字符串ABC。

往回退到2位置,取回C,又后退一个位置,取回B,把C放进1,取回1中的B,向后移动到2,把B放进2.输出字符串ACB。

回退到0,分别取回了BC,把B放入0,取回A,后移到1,把A放进1,后移,把C放进2,输出字符串BAC。

......

可以画一个递归调用树来理解,原理就是使用DFS来进行搜索和剪枝

往回回溯时,执行代码:

book[i] = 0;

【图片转来的】

另一种解法,参考牛客网:

å¾ç说æ

第三行代表Output.Swap B with C,相当于上一个方法中的把C放进B的位置,然后进入下一个DFS,把B放进C的位置。

链接:https://www.nowcoder.com/questionTerminal/fe6b651b66ae47d7acce78ffdd9a96c7
来源:牛客网

import java.util.List;
import java.util.Collections;
import java.util.ArrayList;
 
public class Solution {
    public static void main(String[] args) {
        Solution p = new Solution();
        System.out.println(p.Permutation("abc").toString());
    }
 
    public ArrayList<String> Permutation(String str) {
        List<String> res = new ArrayList<>();
        if (str != null && str.length() > 0) {
            PermutationHelper(str.toCharArray(), 0, res);
            Collections.sort(res);
        }
        return (ArrayList)res;
    }
 
    public void PermutationHelper(char[] cs, int i, List<String> list) {
        if (i == cs.length - 1) {
            String val = String.valueOf(cs);
            if (!list.contains(val))
                list.add(val);
        } else {
            for (int j = i; j < cs.length; j++) {
                swap(cs, i, j);
                PermutationHelper(cs, i+1, list);
                swap(cs, i, j);
            }
        }
    }
 
    public void swap(char[] cs, int i, int j) {
        char temp = cs[i];
        cs[i] = cs[j];
        cs[j] = temp;
    }
}

 这个方法跟上一个方法思路一样,比上一个方法更好理解。

猜你喜欢

转载自blog.csdn.net/raylrnd/article/details/82584865