牛客题目——没有重复项数字的全排列、有重复项数字的全排列


题目1——没有重复项数字的全排列

给出一组数字,返回该数字的所有排列。
要求:空间复杂度为O(n!),时间复杂度O(n!)

示例
输入:[1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

解题思路

最开始就是暴力解法:循环遍历每个数字,然后使用递归将剩下的数字进行全排列,再遍历全排后的结果,与当前的数字合并;当遍历完全部的数字后就能得到最终的全排结果。但是时间复杂度较高。

最合适的方法就是利用递归和回溯。具体做法如下:

  • 先将数组排序,获取字典最小的排列情况;
  • 递归的时候根据当前下表,遍历后续元素,交换二者位置后,进入下一层递归;
  • 处理完一个分支的递归后,将交换的情况再换回来进行回溯,进入其他分支;
  • 当前下标达到数组末尾就是一种排列情况。

递归函数在其定义中直接或间接调用自身的一种方法。它将一个复杂的问题转换为与原问题相似的规模较小的问题来求解。
回溯:指的是在递归的过程中,从某一分支的子问题回到父问题,然后进入父问题的另一子问题分支。

代码实现

import java.util.*;

public class Solution {
    private void swap(ArrayList<Integer> num,int i1,int i2){
        int temp = num.get(i1);
        num.set(i1,num.get(i2));
        num.set(i2,temp);
    }
    public void recursion(ArrayList<ArrayList<Integer>> res,ArrayList<Integer> num,int index){
        //分枝进入结尾,找到一种排列
        //每次添加的时候要new一个新的ArrayList
        if(index == num.size()-1) res.add(new ArrayList(num));
        else{
            for(int i=index;i<num.size();i++){
                //交换两者
                swap(num,i,index);
                //继续往后找
                recursion(res,num,index+1);
                //回溯
                swap(num,i,index);
            }
        }
    }
    public ArrayList<ArrayList<Integer>> permute(int[] num) {
        Arrays.sort(num);
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        ArrayList<Integer> nums = new ArrayList<Integer>();
        for(int i=0;i<num.length;i++) nums.add(num[i]);
        recursion(res,nums,0);
        return res;
    }
}

题目2——有重复项数字的全排列

给出一组可能包含重复项的数字,返回该数字的所有排列。结果以字典升序排列。
要求:空间复杂度为O(n!),时间复杂度O(n!)

示例
输入:[1,1,2]
输出:[[1,1,2],[1,2,1],[2,1,1]]

解题思路

我们将这个问题看作有n个排列成一行的空格,从左往右依次填入给定的n个数,每个数只能使用一次。
每次填数时,我们肯定不能填已经填过的数,所以可以定义一个标记数组来标记已经填过的数:如果这个数没有被标记过,我们就尝试填入,并将其标记,继续填下一个位置;当回溯的时候,要撤销这个位置填的数以及标记,继续尝试其他没有被标记的数。
但是因为数字存在重复问题,我们需要设置一些条件来保证重复数字只会被填入一次。
条件:mark[i] || i>0 && num[i] == num[i-1] && !mark[i-1]
mark[i]表示下标为i的数字已经填过;
i>0 && num[i] == num[i-1] && !mark[i-1]表示此时的数与上一个相等,并且上一个没有访问过(表示是回溯后的数字),避免了重复

代码实现

import java.util.*;

public class Solution {
    boolean[] mark;
    public void recursion(ArrayList<ArrayList<Integer>> res,LinkedList<Integer> nums,int[] num){
        if(nums.size() == num.length) res.add(new ArrayList<Integer>(nums));
        for(int i=0;i<num.length;i++){
            if(mark[i] || i>0 && num[i] == num[i-1] && !mark[i-1]) 
                continue;
            //添加数字
            nums.add(num[i]);
            mark[i] = true; //设置标记
            recursion(res,nums,num);  //寻找下一个数
            //将上一次全排的结果最后一个数移除掉
            nums.removeLast();
            mark[i] = false; //移除掉的数设置为未访问
        }
    }
    public ArrayList<ArrayList<Integer>> permuteUnique(int[] num) {
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        LinkedList<Integer> nums = new LinkedList<Integer>();
        Arrays.sort(num);
        mark = new boolean[num.length];
        recursion(res,nums,num);
        return res;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值