全排列解法总结(Java语言)

最近在准备蓝桥杯,所以今天想对全排列的一些解法进行总结,这样也方便于以后的复习。

样例问题
      这里我选择了一个较为容易理解的题目进行全排列的总结。题目是输入一个字符串,例如"abcd",通过全排列得出该字符串中字符的所有组合方式。

一、迭代法

      思路分析:这题使用的是迭代法,我的思路是首先创建一个old_list来存储上一次循环中保存的所有字符串。然后对old_list进行初始化,将字符串的0号位添加到old_list中,这其实也相当于完成了第一次循环,将第一次循环得到的所有字符串添加到old_list中。之后从1号位对字符串x进行遍历,取出i号位上的值赋值给临时变量temp并创建一个new_list来存放当次循环得到的字符串。这里必须要创建一个new_list来存放当次循环得到的字符串,不能直接存入到old_list,这样的话会造成重复。然后就是循环遍历old_list中的字符串,对于上一次循环得到的字符串,当次循环的字符总共有三种插入方式,分别是:1、在字符串的前面加上字符 2、在字符串的末尾加上字符 3、在字符串的中间加上字符。当遍历结束后,将new_list赋值给old_list,这样便于下一次循环。最后输出结果。

在这里插入图片描述

public static void main(String[] args) {
        String x = "abcd";
        全排列总结 a = new 全排列总结();
        ArrayList sort1 = a.sort1(x);
        System.out.println(sort1.size());
        System.out.println(sort1);
    }
    //迭代法
    public static ArrayList sort1(String x) {
        ArrayList old_list = new ArrayList<>();                 //创建一个List,用来存放上一次循环完成后的字符串
        
        old_list.add(x.charAt(0) + "");                                 //初始化,将0号位的字符存入list
        
        for (int i = 1; i < x.length(); i++) {                          //从一号位开始遍历字符串
            String temp = x.charAt(i) + "";
            ArrayList new_list = new ArrayList<>();             //创建新的list来存放新一轮的字符串
            
            for (String old : old_list) {                               //循环遍历old_list,取出所有字符串
                new_list.add(temp + old);                               //第一种情况,在字符串的前面加上字符
                new_list.add(old + temp);                               //第二种情况,在字符串的末尾加上字符
                for (int j = 1; j < old.length(); j++) {
                    new_list.add(old.substring(0, j) + temp + old.charAt(j));   //第三种情况,在字符串的中间加上字符
                }
            }
            
            old_list = new_list;                                        //将new_list中的字符串存给old_list,开始下一次循环
        }
        return old_list;                                                //返回结果

    }


二、递归法

      思路分析:这题使用的是递归法,是迭代法的改编版。因为是递归法,所以肯定要将一部分的事情交给现在做,另一部分交给递归去完成。所以我们可以先将字符串x的0~n-1号位上的字符排列出所有情况,然后再将n号位上的字符插入到所有情况中,这样就完成了 全排列。如下面代码所示。先创建一个old_list来存放第n个字符插入后的所有情况。然后调用递归,获取0~n-1位上字符串的所有排列情况。然后将第n号位上的字符插入进去即可。插入方法有三种情况,同迭代法是一样的。最后返回结果

public static void main(String[] args) {
        String x = "abcd";
        全排列总结 a = new 全排列总结();
        ArrayList arr = new ArrayList<>();
        ArrayList sort2 = a.sort2( x, x.length()-1, arr);
        System.out.println(sort2.size());
        System.out.println(sort2);
    }
    // 递归法
    public ArrayList sort2(String x, int n, ArrayList arr) {
        if (n == 0) {                                       //为递归设置出口
            arr.add(x.charAt(0) + "");                      //初始化list
            return arr;
        }
        ArrayList old_list = new ArrayList<>();         //为n号位字符创建一个list
        ArrayList new_list = sort2(x, n - 1, arr);      //调用递归,得到1~n-1号位已经排列好全部情况的list

        for (String old : new_list) {                           // 循环遍历new_list,将字符串n号位的字符插入到全部情况中
            old_list.add(x.charAt(n) + old);                    // 第一种情况,在字符串的前面加上字符
            old_list.add(old + x.charAt(n));                    // 第二种情况,在字符串的末尾加上字符
            for (int j = 1; j < old.length(); j++) {
                old_list.add(old.substring(0, j) + x.charAt(n) + old.charAt(j)); // 第三种情况,在字符串的中间加上字符
            }
        }

        return old_list;                                        //返回结果
    }


三、交换法

      思路分析:这题使用的是交换法,运用了回溯的思想。首先是将字符串转换为字符数组,并排列。然后调用sort3方法,每次都循环遍历从k号位到字符串末尾的每一个字符。swap(arr,k,i)意味将第i号位上的的值,交换到第k号位。然后递归调用,直到当k==arr.length时就将排列好的arr添加到全局变量res中,完成一次排列。这里调用完之后必须swap(arr,i,k)将数组复原,然后进行下一次循环。最后得到结果

在这里插入图片描述

    public static void main(String[] args) {
        String x = "abcd";
        全排列总结 a = new 全排列总结();
        char[] array = a.toArray(x);
        ArrayList sort3 = a.sort3(array, 0);
        System.out.println(sort3.size());
        System.out.println(sort3);
    }
    // 交换法
    public char[] toArray(String x) {                           //将字符串转换为字符数组
        char[] arr = x.toCharArray();
        Arrays.sort(arr);
        return arr;
    }
    
    ArrayList res = new ArrayList<>();                  //定义一个全局变量来存放所有排列情况

    public ArrayList sort3(char[] arr, int k) {
        if (k == arr.length) {
            res.add(new String(arr));
        }
        for (int i = k; i < arr.length; i++) {                  //每次都循环遍历每一个未被遍历过的数
            swap(arr, k, i);                                    //将第i号位上的值和k号位上的值交换
            
            sort3(arr, k + 1);                                  //从k+1号位开始调用递归,知道遍历完所有数
            
            swap(arr, i, k);                                    //回溯,将字符串复原
        }
        return res;

    }

    public void swap(char[] arr, int k, int i) {                //交换位置
        char temp = arr[k];
        arr[k] = arr[i];
        arr[i] = temp;
    }


四、前缀法

      思路分析:这题使用的是前缀法,我为本题加了一个条件(字符串必须有序),在全排列中,按照顺序排列,并且输出第num个排列好的数。Main方法调用sort4(),进入for循环,每次都遍历数组全部,然后遇到if语句,判断数组中某字符的出现次数如果大于在字符串中出现的次数,那说明需要把字符添加到prefix中。最后当字符串prefix的长度等于数组的长度时,即一次排列完成,总数加一。当cnt==num时则输出结果。

    public static void main(String[] args) {
        String x = "abcd";
        全排列总结 a = new 全排列总结();
        a.sort4("",x.toCharArray());
    }
    int cnt=0;                                      //总数
    final int num=3;                                //需要获得第几个的值
    //前缀法
    public void sort4(String prefix,char[] arr) {
        if(prefix.length()==arr.length) {                       //递归出口
            cnt++;
            if(cnt==num) {                                      //当字符串prefix的长度等于数组的长度时
                System.out.println(prefix);                     //输出结果
                System.exit(0);;
            }
        }
        for(int i=0;i

你可能感兴趣的:(全排列解法总结(Java语言))