剑指 offer 38 字符串的排列(回溯)

1 题目描述

 

2 算法思路

思路:深度优先遍历,先固定第一个字符,再固定第二个字符,最后固定第n个字符,然后回溯得到所有情况。

剪枝:当字符串中存在重复的字符时,就可以会出现重复的方案。因此在固定字符时,可以确保每个字符只在此处固定一次,即遇到重复的字符不交换,直接跳过。

递归流程:

  1. 当x = len(c) - 1 ,就代表固定完所有位了,将当前组合添加到res
  2. 递归参数:当前的固定位x
  3. 递归工作:初始化一个Set ,用来存储已经固定的字符,可以排除重复的字符,将第x位字符和 后面的字符分别进行交换,进入下层递归
    1. 剪枝:当c[i] 存在于Set中,就代表是重复字符,应该直接跳过
    2. 将c[i] 加入Set,便于后续剪枝
    3. 固定字符:将字符c[i] 和 c[x] 交换,也就是固定c[i]为当前字符
    4. 开启下层递归:dfs(x + 1),开始固定第x +1 个字符
    5. 回溯:将c[i] 和 c[x] 交换 ,也就是还原之前的交换,向上回溯

再总结:本题的解法,是通过固定x位置的字符,然后利用一个set来剪枝。

在进行排列组合时,使用的是交换, 即让第一个字符分别和每一个字符交换,然后递归进行,让下一个字符也进行交换,如此一来就会得到所有的组合。

3 代码

List<String> list = new ArrayList<>();
    //为了让递归函数添加结果方便,定义到函数之外,这样无需带到递归函数的参数列表中
    char[] c;
    //同;但是其赋值依赖c,定义声明分开
    public String[] permutation(String s) {
        c = s.toCharArray();
        //从第一层开始递归
        dfs(0);
        return list.toArray(new String[list.size()]);
        //将字符串数组ArrayList转化为String类型数组
    }

    private void dfs(int x) {
        //当递归函数到达第三层,就返回,因为此时第二第三个位置已经发生了交换
        if (x == c.length - 1) {
            list.add(String.valueOf(c));//将字符数组转换为字符串
            return;
        }
        //为了防止同一层递归出现重复元素
        HashSet<Character> set = new HashSet<>();
        //这里就很巧妙了,第一层可以是a,b,c那么就有三种情况,这里i = x,正巧dfs(0),正好i = 0开始
        // 当第二层只有两种情况,dfs(1)i = 1开始
        for (int i = x; i < c.length; i++){
            //发生剪枝,当包含这个元素的时候,直接跳过
            if (set.contains(c[i])){
                continue;
            }
            set.add(c[i]);
            //交换元素,这里很是巧妙,当在第二层dfs(1),x = 1,那么i = 1或者 2, 要不是交换1和1,要不交换1和2
            swap(i,x);
            //进入下一层递归
            dfs(x + 1);
            //返回时交换回来,这样保证到达第1层的时候,一直都是abc。这里捋顺一下,开始一直都是abc,那么第一位置总共就3个位置
            //分别是a与a交换,这个就相当于 x = 0, i = 0;
            //     a与b交换            x = 0, i = 1;
            //     a与c交换            x = 0, i = 2;
            //就相当于上图中开始的三条路径
            //第一个元素固定后,每个引出两条路径,
            //     b与b交换            x = 1, i = 1;
            //     b与c交换            x = 1, i = 2;
            //所以,结合上图,在每条路径上标注上i的值,就会非常容易好理解了
            swap(i,x);
        }
    }

    private void swap(int i, int x) {
        char temp = c[i];
        c[i] = c[x];
        c[x] = temp;
    }

 

4 提交结果

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值