解1:全排序 [ 递归形式 ]

所谓全排列,就是打印出字符串中所有字符的所有排列。例如输入字符串 abc ,则打印出 a、b、c 所能排列出来的所有字符串 abc、acb、bac、bca、cabcba

从集合中依次选出每一个元素,作为排列的第一个元素,然后对 剩余 的元素进行 全排列 ,如此递归处理,从而得到所有元素的全排列。

以对字符串 abc 进行全排列为例,我们可以这么做:

  1. 固定 a,求后面 bc 的排列:abc,acb,求好后,ab 交换,得到 bac
  2. 固定 b,求后面 ac 的排列:bac,bca,求好后,ac 交换,得到 cba
  3. 固定 c,求后面 ba 的排列:cba,cab

整理代码

  1. public class Solution {
  2. public static void permutation(char[] str , int first,int end) {
  3. //输出一个排列方式
  4. if(first == end) {
  5. for(char c:str)
  6. System.out.print(c);
  7. System.out.println();
  8. }
  9. for(int i = first; i <= end ; i++) {
  10. swap(str, i, first);
  11. permutation(str, first+1, end); //固定好当前一位,继续排列后面的
  12. swap(str, i, first);
  13. }
  14. }
  15. private static void swap(char[] str, int i, int first) {
  16. char tmp;
  17. tmp = str[first];
  18. str[first] = str[i];
  19. str[i] = tmp;
  20. }
  21. public static void main(String[] args) {
  22. char[] str = {'a','b','c'};
  23. permutation(str, 0, 2); //输出str[0..2]的所有排列方式
  24. }
  25. }

解2:去重全排列

从集合中依次选出每一个元素,作为排列的第一个元素,然后对 剩余 的元素进行 全排列 ,如此递归处理,从而得到所有元素的全排列。

为了杜绝重复的全排列,我们只要 保证 作为排列的第一个元素是 不重复 的,就可以保证全排列是不重复的。

用编程的话描述就是第 i 个数与第 j 个数交换时,要求 **[i,j)** 中没有与第 j 个数相等的数。

整理代码

public class Solution {

    public static void permutation(char[] str , int first,int end) {
        //输出一个排列方式
        if(first == end) {
            for(char c:str)
                System.out.print(c);
            System.out.println();
        }

        for(int i = first; i <= end ; i++) {
           # 去重关键加上能否交换的判断即可
           if(can_swap(str,first,i)){
               swap(str, i, first);
               permutation(str, first+1, end);  //固定好当前一位,继续排列后面的
               swap(str, i, first);
           }
        }
    }

    private static boolean can_swap(char[] str, int i, int j) {
        while(i<j){
            if(str[i++]==str[j])return false;
        }
        return true;
    }

    private static void swap(char[] str, int i, int first) {
        char tmp;
        tmp = str[first];
        str[first] = str[i];
        str[i] = tmp;
    }

    public static void main(String[] args) {
        char[] str = {'a','b','b'};
        permutation(str, 0, 2);             //输出str[0..2]的所有排列方式
    }
}

解3:next_permutation [ STL 字典全排列 ]一定不会重复

image.png
image.png
image.png

整理代码

public boolean nextPermutation(char[] arr) {
    int i = arr.length - 2;
    //1. 找出 i<ii 的部分
    while (i >= 0 && arr[i] >= arr[i + 1]) {
        i--;
    }
    if (i < 0) {
        return false;
    }
    int j = arr.length - 1;
    //2. 找出 j>i 的部分
    while (j >= 0 && arr[i] >= arr[j]) {
        j--;
    }
    //3. 交换(i,j)
    swap(arr, i, j);
    //4. 反转ii 之后元素
    reverse(arr, i + 1);
    return true;
}

整理代码

public class Solution {

    public static boolean next_permutation(char[] str , int first,int last) {
        if(first>last||first==last)return false;    //只有一个元素
        int i=last;                                 //i指向尾端
        while(true){
            int ii=i;
            //1. 找 i<ii
            --i;
            //以上,锁定一组(两个相邻元素)
            if(str[i]<str[ii]){
                int j=last+1;                       //令 j 指向尾端的下一个越界结点
                //2. j>i
                while(!(str[i]<str[--j]));          //由尾端向前找,直到遇上比下标 i 大的元素
                swap(str,i,j);                      //交换 i,j
                reverse(str,ii,last);               //将ii之后元素全部逆向重排
                return true;
            }
            if(i==first){                           //进行至最前面了
                reverse(str,first,last);            //全部逆向重排
                return false;                       
            }
        }
    }

    private static void reverse(char[] str, int first, int last){
        while(first<last){
            swap(str,first,last);
            first++;
            last--;
        }
    }

    private static void swap(char[] str, int i, int j) {
        char tmp;
        tmp = str[j];
        str[j] = str[i];
        str[i] = tmp;
    }

    public static void main(String[] args) {
        //1. 非字典排序的数组
        char[]str=new char[]{'c','a','b'};
        //2. 将其字典排序成 a,b,c 
        Arrays.sort(str);
        //3,如果有下一个排序序列将其输出 [全输出完就是字典全排序]
        do{
            for(char c:str){
                System.out.print(c+" ");
            }
            System.out.println();
        } while(next_permutation(str,0,2));

    }
}

解4:prev_permutation

image.png
image.png
image.png

整理代码

public class Solution {

    public static boolean prev_permutation(char[] str , int first,int last) {
        if(first>last||first==last)return false;    
        int i=last;                                 
        while(true){
            int ii=i;
            --i;
            if(str[i]>str[ii]){                        //差异1:str[i]<str[ii]
                int j=last+1;                      
                while(!(str[i]>str[--j]));          //差异2: while(!(str[i]<str[--j])); 
                swap(str,i,j);                      
                reverse(str,ii,last);               
                return true;
            }
            if(i==first){                         
                reverse(str,first,last);           
                return false;
            }
        }
    }

    private static void reverse(char[] str, int first, int last){
        while(first<last){
            swap(str,first,last);
            first++;
            last--;
        }
    }

    private static void swap(char[] str, int i, int j) {
        char tmp;
        tmp = str[j];
        str[j] = str[i];
        str[i] = tmp;
    }

    public static void main(String[] args) {
        //1. 非字典排序的数组
        char[]str=new char[]{'c','b','a'};
        //2. 如果有下一个排序序列将其输出
        do{
            for(char c:str){
                System.out.print(c+" ");
            }
            System.out.println();
        } while(prev_permutation(str,0,2));
    }
}

解5:全组合

求全组合,则假设原有元素 🍗全排列、全组合合集整理 - 图7 个,则最终组合结果是 🍗全排列、全组合合集整理 - 图8

用位操作方法:假设元素原本有:a,b,c 三个,则 1 表示取该元素,0 表示不取。(3 位表示 8 种可能性

故取 a 则是 001,取 ab 则是 011

通过 **>>** 操作依次和 1&运算 来判断是否包含某位的字符

例如:求解 ac 101

  1. 5= **101 &1** =1 得出 包含 a
  2. 对数字 5>>1 操作,2= **10&1** =0 得出 不包 b
  3. 对数字 2 >>1 操作,1= **1&1** =1 得出 包含 c

算法思想

  - 首先遍历每一种情况
  - 针对每一个情况通过 通过 `**>>**` 操作依次和 **1** 做 **&运算 **来判断每一位的字符

整理代码

public class Solution {


    public static  void Combination( ) {
        char[] str = {'a' , 'b' ,'c'};
        int n = str.length;           
        //1. “<<” 表示 左移:各二进位全部左移若干位,高位丢弃,低位补0。:即求1左移n位的2^n。
        int nbit = 1<<n;                                     
        System.out.println("全组合结果个数为:"+nbit);
        //2. 结果有nbit个。输出结果从数字小到大输出:即输出0,1,2,3,....2^n。
        for(int i=0 ;i<nbit ; i++) {                        
            System.out.print("组合数值  "+i + " 对应编码为: ");
            int tmp=i;
            //3. 通过 >> 操作判断是否包含第j位字符
            for(int j=0; j<n ; j++) {                 
                if((tmp & 1)!=0) {                            
                    System.out.print(str[j]);
                }
                tmp=tmp>>1;
                if(tmp==0)break;
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        Combination();
    }
}