剑指offer系列-----item27 字符串的排列(究极无敌多种java回溯+剪枝做法)

题干:

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

思路:

这道题思路明显,全排列问题的解决核心就是利用回溯法。只不过这道题全排列的结果可能会包含重复的值,所以还需要应用剪枝来去重,所以下面的几种解法都是利用回溯法思想,只不过剪枝的操作略有不同。

具体的多种解法见下:

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class Solution {
    public  ArrayList<String> Permutation(String str) {
        ArrayList<String> list1 = new ArrayList<String>();
        if(str.length()==0){
            return list1;
        }
        List<List<String>> list = new ArrayList<>();
        boolean flag[]=new boolean[str.length()];
        String[] strings = str.split("");
        Stack<String> stack = new Stack<String>();
        quanpailie(strings,flag,0,strings.length,list,stack);
        for(int i=0;i<list.size();i++){
            String temp = list.get(i).toString();
            String[] temp1 = temp.substring(1,temp.length()-1).split(", ");
            System.out.println(temp1.length);
            StringBuffer SB = new StringBuffer();
            for(String ss:temp1){
                SB.append(ss);
            }
            if(!list1.contains(SB.toString())){
                //System.out.println(SB.toString().length());
                list1.add(SB.toString());
            }


        }
        return list1;
    }

    public void quanpailie(String[] str, boolean[] flag, int cursize, int len, List<List<String>> list, Stack<String> stack){//这里的回溯算法入口的参数过多了,可以设置为私有变量private,从而不需要传入这么多的入参。
        if(cursize==len){
            list.add(new ArrayList<String>(stack));
            return;
        }
        else {
            for(int i=0;i<len;i++){
                if(!flag[i]){
                    stack.push(str[i]);
                    flag[i]=true;
                    quanpailie(str,flag,cursize+1,len,list,stack);
                    flag[i]=false;//状态重置
                    stack.pop();//状态重置
                }
            }
        }
    }
}

可以明显看出,上面这个代码非常的臃肿繁杂,简化一下:

private boolean flag[]=new boolean[9];
private List<List<String>> list = new ArrayList<>();
private Stack<String> stack = new Stack<String>();
public  ArrayList<String> Permutation(String str) {
    ArrayList<String> list1 = new ArrayList<String>();
    if(str.length()==0){
        return list1;
    }
    //List> list = new ArrayList<>();
    //boolean flag[]=new boolean[str.length()];
    String[] strings = str.split("");
    //Stack stack = new Stack();
    quanpailie(strings,0,strings.length);
    for(int i=0;i<list.size();i++){
        String temp = list.get(i).toString();
        String[] temp1 = temp.substring(1,temp.length()-1).split(", ");
        System.out.println(temp1.length);
        StringBuffer SB = new StringBuffer();
        for(String ss:temp1){
            SB.append(ss);
        }
        if(!list1.contains(SB.toString())){
            //System.out.println(SB.toString().length());
            list1.add(SB.toString());
        }
    }
    return list1;
}


public void quanpailie(String[] str, int cursize, int len){
    if(cursize==len){
        list.add(new ArrayList<String>(stack));
        return;
    }
    else {
        for(int i=0;i<len;i++){
            if(!flag[i]){
                stack.push(str[i]);
                flag[i]=true;
                quanpailie(str,cursize+1,len);
                flag[i]=false;//状态重置
                stack.pop();//状态重置
            }
        }
    }
}

进一步简化:

private boolean flag[]=new boolean[9];
private ArrayList<String> list = new ArrayList<>();
private StringBuffer sb = new StringBuffer();

public  ArrayList<String> Permutation(String str) {
    if(str.length()==0){
        return list;
    }
    String[] strings = str.split("");
    quanpailie(strings,0,strings.length);
    return list;
}

//最核心的就是回溯函数(本质就是递归)以及状态的重置
public void quanpailie(String[] str, int cursize, int len){
    if(cursize==len){
        if(!list.contains(sb.toString())){//去重(剪枝),时间复杂度过高,在leetcode会超时
            list.add(sb.toString());
        }
        return;
    }
    else {
        for(int i=0;i<len;i++){
            if(!flag[i]){
                sb.append(str[i]);
                flag[i]=true;
                quanpailie(str,cursize+1,len);
                flag[i]=false;//状态重置
                sb.deleteCharAt(sb.length()-1);//状态重置
            }
        }
    }
}

针对上面剪枝的时间复杂度过高的问题,继续优化如下,利用TreeSet完成去重操作:

class Solution {
    private TreeSet<String> set = new TreeSet<>();//利用TreeSet执行去重效果,因为其本身就不支持重复的元素
    private StringBuffer sb = new StringBuffer();
    private boolean flag[] = new boolean[9];
    public String[] permutation(String s) {
        String[] strings = s.split("");
        if(s==null || s.length()==0){
            return strings;
        }
        quanpailie(strings,0,strings.length);
        String [] res = new String[set.size()];
        int i=0;
        for(String s1:set){
            res[i]=s1;
            i++;
        }
        return res;
    }


    public void quanpailie(String[] strings, int current, int len){
        if(current==len){
            set.add(sb.toString());
            return;
        }
        else {
            for(int i=0;i<len;i++){
                if(!flag[i]){
                    sb.append(strings[i]);
                    flag[i]=true;
                    quanpailie(strings,current+1,len);
                    flag[i]=false;
                    sb.deleteCharAt(sb.length()-1);
                }
            }
        }
    }
}

还可以继续优化,在回溯的期间完成剪枝操作:

//上面两种去重操作的时间复杂度都有一些大,这里先将数组排序,然后在添加的时候判断,时间复杂度低一些
class Solution {
    private ArrayList<String> list = new ArrayList<>();
    private StringBuffer sb = new StringBuffer();
    private boolean flag[] = new boolean[9];
    public String[] permutation(String s) {
        String[] strings = s.split("");
        char [] c = s.toCharArray();
        Arrays.sort(c);//这里的排序操作是为了后面的去重
        if(s==null || s.length()==0){
            return strings;
        }
        quanpailie(c,0,c.length);
        String [] res = new String[list.size()];
        int i=0;
        for(String s1:list){
            res[i]=s1;
            i++;
        }
        return res;
    }


    public void quanpailie(char[] c, int current, int len){
        if(current==len){
            list.add(sb.toString());
            return;
        }
        else {
            for(int i=0;i<len;i++){
                if(!flag[i]){
                    if(i>0 && c[i]==c[i-1] && !flag[i-1]){//去重操作(剪枝操作)
                        continue;
                    }
                    sb.append(c[i]);
                    flag[i]=true;
                    quanpailie(c,current+1,len);
                    flag[i]=false;
                    sb.deleteCharAt(sb.length()-1);
                }
            }
        }
    }
}

你可能感兴趣的:(算法,JAVA,数据结构,字符串,剪枝,算法,java,leetcode)