全排列

示例1
求任意不同的n个数的全排列

先假设4个序列的全排列
在这里插入图片描述
递归过程其实就是忽略开头元素,去求解一个比自己当前阶数少一阶的序列的全排列,而这个子问题的全排列的解决也是一个同样的过程,这样就完全符合递归思想了。我们只要在每一个进程中,让当前规模下序列的每一个元素做一遍开头,然后进入递归调用即可解决问题


代码如下

public class Main
{
	int[] data = {1,2,3,4,5,6,8,9,10,32,15,18,33};/*本例设n为10*/
    int num = 0;/*统计全排列的个数*/
    void Perm(int begin, int end){
        int i;
        if(begin == end){/*当开始位置下标和结束位置下标相等时,意味着当前的数组中只有一个元素了,即递归结束,产生一个全排列*/
                         //如有必要,可在此打印出这个全排列或作其它处理
            num++;/*统计全排列的个数*/
        }
        else for(i = begin; i <= end; i++){
            swap(data[begin], data[i]);//移动位置,做出移动操作
            Perm(begin+1, end);//进行递归调用,将开头加一,代表不去考虑第一个元素,进一步去考虑剩下的元素。其实就是去解决剩余元素的全排列
            swap(data[begin], data[i]);//问题解决完之后,恢复,用于下一次交换
        }
    }
    static void swap(int a, int b) {//交换两个变量值
        int t = a;
        a = b;
        b = t;
    }
    public static void main(String args[]){
        Main m = new Main();
        m.Perm(0,9);//求十个数的全排列
        System.out.println(m.num);//输出总数num= 3628800
    }
}	 



示例2
求 1~n 的全排列

其基本思想如以下的解答树
这里插入图片描述
即先输出所有以1开头的就排列,然后输出以2开头的排列…最后是n

以1开头的排列的特点是:第一位是1,后面是2-9的排列(n=9)。根据字典序的定义,这些2~9的就排列也必须是字典序的排列。


代码如下

class Solution {//此代码中数组nums的元素是任意且不重复的,不只是解决1~n的排序
    List<List<Integer>> list = new ArrayList<>();//用来存储所有的排列情况

    public List<List<Integer>> permute(int[] nums) {//nums是需排列的数组
        int n = nums.length;
        Qpl(n, nums, new ArrayList(), 0);
        return this.list;
    }

    private void Qpl(int n, int[] nums, List A, int cur) {//n是数组的大小,集合A用来存储排列好的序列,cur是当前元素的位置
        if (cur == n) {//递归结束条件
            list.add(new ArrayList(A));
            return;
        } else for (int i = 0; i < nums.length; i++) {//尝试在A[cur]中填入各种整数i
            int ok = 1;//标志变量,若集合A中包含某个i,则为0,反之为1
            for (int j = 0; j < cur; j++) {
                if ((int)A.get(j) == nums[i]) ok = 0;//如果i已在A[0]~A[cur-1]出现过,则不选
            }
            if (ok != 0) {
                A.add(nums[i]);
                Qpl(n, nums, A, cur + 1);
                A.remove(A.size()-1);//递归结束后,恢复
            }
        }
    }
}



示例3
生成可重集的排列

其基本思想大致如示例2

只不过需要统计 A[0]~A[cur-1] 中 P[i] 的出现次数c1,以及P数组中 P[i] 的出现次数c2,只要 c1<c2 ,就可以递归调用
但是,枚举的下标 i 应不重复、不遗漏地取遍所有 P[i] 的值,所以我们可以先对数组P进行排序, 然后检查P的第一个元素和所有“与前一个元素不相同”的元素


代码如下

class Solution2 {
        List<List<Integer>> list = new ArrayList<>();

        public List<List<Integer>> permute(int[] nums) {
            int n = nums.length;
            Arrays.sort(nums);//在此排序以便检查是否重复取P[i]
            Qpl(n, nums, new ArrayList(), 0);
            return this.list;
        }

        private void Qpl(int n, int[] nums, List A, int cur) {
            if (cur == n) {
                list.add(new ArrayList(A));
                return;
            } else for (int i = 0; i < nums.length; i++)
                if(i==0||nums[i]!=nums[i-1]){//保证i不重复,不遗漏地取遍所有P[i]的值,即检查P的第一个元素和所有“与前一个元素不相同”的元素
                    int c1=0,c2=0;
                    for (int j = 0; j < cur; j++) {
                        if ((int)A.get(j) == nums[i]) c1++;//统计A[0]~A[cur-1] 中P[i]的出现次数
                    }
                    for(int j=0; j<nums.length; j++){
                        if(nums[j] == nums[i]) c2++;//统计数组P中P[i]的出现次数
                    }
                    if (c1 < c2) {//只要 c1<c2 就可递归调用
                        A.add(nums[i]);
                        Qpl(n, nums, A, cur + 1);
                        A.remove(A.size()-1);
                    }
                }
        }
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值