算法之全排列

剑指 Offer 38. 字符串的排列

输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]

经典全排列的题目,全排列的应用场景很多,所以一定要熟练
本质就是回溯,通过交换元素来实现全排列!
算法之全排列_第1张图片
上图中第一位其实也是递归的第一层,三个数a,b,c分别是第一位要与之交换的数,for循环遍历
以此类推,第二位也是递归的第二层,第三位是递归的第三层。
递归的终止条件就是递归的层数是否等于字符数组的大小

class Solution {
    HashSet<String> ss=new HashSet<>();
    char[] a;
    public String[] permutation(String s) {
        if(s.equals("")) return new String[]{""};
        a=s.toCharArray();
        f(0);
//        String[] res=new String[ss.size()];
//        int j=0;
//        for(String i:ss){
//            res[j++]=i;
//        }
//        return res;
        //集合都可以直接转化为数组,所以可以进一步优化
        return ss.toArray(new String[ss.size()]);
    }

    private void f(int k) {
        if(k==a.length-1){
//            StringBuilder s=new StringBuilder();
//            for(int i=0;i
//                s.append(a[i]);
//            }
//            ss.add(s.toString());
            //字符数组也可以通过String的类方法直接转化为字符串!不要忘记怎么转!
            ss.add(String.valueOf(a));
            return;
        }
        for(int i=k;i<a.length;i++){
            char t=a[i];a[i]=a[k];a[k]=t;  //交换元素
            f(k+1);  //递归,固定下一位
            t=a[i];a[i]=a[k];a[k]=t;  //回溯,交换回去
        }
    }
}

进一步优化,剪枝,虽然可以通过set来去重,但在产生全排列的过程中还是会产生很多重复的排列,会浪费大量时间
因此可以在交换时判断一下这个数是否之前已经交换过了,交换过就可以直接去找下一个数交换,即剪枝
没交换过就继续交换,并将这个数标记一下,此处用set来标记,即已经交换过的数就丢进set

private void f(int k) {
        if(k==a.length-1){
            ss.add(String.valueOf(a));
            return;
        }
//        for(int i=k;i
//            char t=a[i];a[i]=a[k];a[k]=t;
//            f(k+1);
//            t=a[i];a[i]=a[k];a[k]=t;
//        }
        //可以在这里新建一个set来剪枝
        HashSet<Character> set=new HashSet<>();
        for(int i=k;i<a.length;i++){
            if(set.contains(a[i])) continue;  
            set.add(a[i]);  
            char t=a[i];a[i]=a[k];a[k]=t;
            f(k+1);
            t=a[i];a[i]=a[k];a[k]=t;
        }
    }

你可能感兴趣的:(算法,leetcode,java)