面试题 08.08. 有重复字符串的排列组合(回溯解法)

本文介绍了一种使用回溯法解决有重复字符的排列组合问题的解法,通过boolean数组标记已使用元素,避免重复。在排序后,检查相邻元素是否相同且已使用,若相同则跳过。同时提到了另一种涉及元素交换的解法,但这种方法较为复杂且非标准回溯模板。

1.题目

在这里插入图片描述

2.解法1(不需要交换元素)

class Solution{
    public String[] permutation(String s){
        int len = s.length();

        if(s == null || len == 0){
            return new String[0];
        }

        // 根据char对应得ASCII码进行排序
        char[] charArr = s.toCharArray();

        // 将重复得元素排到一起
         Arrays.sort(charArr);

        // 存放结果
        List<String> result = new ArrayList<>();

        // 进行字符串得拼接
        StringBuilder sb = new StringBuilder(len);

        boolean[] used = new boolean[len];

        int count = 0;

        backtrack(charArr, count, len, used, sb, result);

        return result.toArray(new String[0]);

    }

    public void backtrack(char[] arr
                        , int count
                        , int len
                        , boolean[] used
                        , StringBuilder sb
                        , List<String> result){

        // 递归结束的条件
        if(count == len){
            result.add(sb.toString());
            return;
        }

        for(int i = 0; i < len; i++){
            // 如果没有用过
            if(!used[i]){

                // 回溯的条件,去除重复元素
                if(i > 0 && arr[i] == arr[i-1] && !used[i-1]){
                    continue;
                }

                sb.append(arr[i]);
                used[i] = true;
                backtrack(arr, count+1, len, used, sb, result);
                // 因为i和sb中对应的count位置不一样
                sb.deleteCharAt(count);
                used[i] = false;
            }
        }
    }
}

3.思路

1.使用一个boolean数组标记一下哪些元素使用过,如果已经使用就不需要再用
2.因为重复字符对应的情况一样,所以先使用Array.sort()进行排序,然后判断
if(i>0 && arr[i-1] == arr[i] && !used[i-1]){
continue;
}
你想想现在轮到你了,但是和你值一样得字符为false,说明什么,说明它已经被处理过了,相同字符无需再进行处理。

4.还有一种方法(交换元素)

class Solution{
    public String[] permutation(String s){
        int len = s.length();

        if(s == null || len == 0){
            return new String[0];
        }

        // 根据char对应得ASCII码进行排序
        char[] charArr = s.toCharArray();

        // 将重复得元素排到一起
         Arrays.sort(charArr);

        // 存放结果
        List<String> result = new ArrayList<>();

        backtrack(charArr, 0, len, result);

        return result.toArray(new String[0]);

    }

    public void backtrack(char[] arr
                        , int start
                        , int len
                        , List<String> result){

        // 递归结束的条件 注意这里是len-1
        if(start == len-1){
            // 将数组排列成想要的样子
            result.add(new String(arr));
            return;
        }

        for(int i = start; i < len; i++){
            // 去除重复的元素, 注意边界条件i从start开始的
            // 避免 aba  baa 这种情况
            if(i > start && (arr[start] == arr[i] || arr[i] == arr[i-1])){
                continue;
            }

            // 交换元素
            swap(arr, i, start);
            // 注意这里不是i+1,因为i已经交换到start的位置,所以从start开始
            backtrack(arr, start+1, len, result);
            swap(arr, i, start);
            }
        }
    
    public void swap(char[] arr, int i, int j){
        char ch = arr[i];
        arr[i] = arr[j];
        arr[j] = ch;
    }
}

这种方法,需要注意的细节比较多,很容易出错,没有上一种方法简单,而且也不是回溯解法的标准模板,只有解法1是回溯解法的标准模板。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值