不重复元素的排列和包含重复元素的排列 采用深度优先搜索方法

参考链接:https://leetcode-cn.com/problems/permutations-ii/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liwe-2/

1.全排列

题目描述

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

思路

参考链接
https://leetcode-cn.com/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/
不重复元素的排列和包含重复元素的排列 采用深度优先搜索方法_第1张图片整个流程类似于一个棵决策树的遍历,采用深度优先搜索策略。在实现的时候主要有两点需要注意

  1. 使用used[len]数组,来保存已经访问过的数值
  2. 在返回当前层函数之前,记得回退,撤销当前path和used的访问信息。
    List> res;  // 记录path的集合

    public List> permute(int[] nums) {
        
        // 空值处理
        if(nums.length==0)
            return new ArrayList<>();

        int len = nums.length;     
        res = new ArrayList<>();
        List path = new ArrayList();
        
        boolean[] used = new boolean[len];   // 记录是否访问过
        dfs(nums, len, 0, path, used);
        return res;
    }

    public void dfs(int[] nums, int len, int depth, List path, boolean[] used){
        if(depth==len)
            res.add(new ArrayList(path));  // 拷贝一个对象添加到res集合中

        // 搜索数组中访问的数值
        for(int i=0; i

2.包含重复数值的全排列

题目描述

给定一个可包含重复数字的序列,返回所有不重复的全排列。

思路

这道题目与第一道题目的区别在于,在暴力所有所有情况,存在很多重复的排列。解题方法,在第一提的基础上,考虑如何去重。这里为了去重做了两件事情

  1. 排序,首先对数组进行排序,让相同的数值相邻,成为兄弟节点
  2. 设定了一个条件if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false判断来筛选、过滤掉相等数值区间除了第一个数值之外的数值。

至于排序去重的作用,大佬做出了解释。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200602145111875.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FjaG9uZ18yMDUw,size_16,color_FFFFFF,t_70

不重复元素的排列和包含重复元素的排列 采用深度优先搜索方法_第2张图片对于重复数值,剪纸的情况来看,我们需要减掉的是横向的相邻的、相等的节点,而不是纵向的、相等节点,因此可以理解为什么另外要求 u s e d [ i − 1 ] = = f a l s e used[i-1]==false used[i1]==false

代码

    public List> res;

    public List> permuteUnique(int[] nums) {

        res = new ArrayList<>();
        int len = nums.length;

        List path = new ArrayList();
        boolean[] used = new boolean[len];

        Arrays.sort(nums);  // 排序

        dfs(nums, len, 0, path, used);
        return res;
    }

    public void dfs(int[] nums, int len, int depth, List path, boolean[] used){
        
        if(nums.length==depth){
            res.add(new ArrayList(path));
            return;
        }

        for(int i=0; i0&&nums[i]==nums[i-1]&&used[i-1]==false)
                    continue;
                path.add(nums[i]);
                used[i]=true;

                dfs(nums, len, depth+1, path, used);

                path.remove(path.size()-1);
                used[i]=false;
            }
        }

    }

面试题38. 字符串的排列

题目描述

输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
示例:
输入:s = “abc”
输出:[“abc”,“acb”,“bac”,“bca”,“cab”,“cba”]

思路

这道题目本质上与上面一题一样,是找出包含相同元素的排列问题,与上面题目的区别在于,这里需要排列的是字符。

代码

    public List res = new ArrayList<>();

    public String[] permutation(String s) {

        //  空值处理
        if(s.isEmpty())
            return new String[0];
        
        // 字符串转成字符数组,便于随机访问
        char[] charArray = s.toCharArray();
        int len = charArray.length;
        // path定义为字符串构造器,便于修改字符
        StringBuilder path = new StringBuilder();
        boolean[] used = new boolean[len];
        // 为了便于去重
        Arrays.sort(charArray);

        dfs(charArray, len, 0, path, used);

        return res.toArray(new String[0]);  // list转成字符串数组
    }

    public void dfs(char[] charArray, int len, int depth, StringBuilder path, boolean[] used){
        // 越过叶子节点时,输出path到res
        if(depth==len){
            res.add(path.toString());
        }
        // 遍历未访问过的节点
        for(int i=0; i0&&charArray[i]==charArray[i-1]&&used[i-1]==false)
                    continue;
                path.append(charArray[i]);
                used[i] = true;

                dfs(charArray, len, depth+1, path, used);

                path.deleteCharAt(path.length()-1);
                used[i] = false;
            }
        }
    }

你可能感兴趣的:(递归)