LeetCode算法题解(回溯)|LeetCode491. 递增子序列、LeetCode46. 全排列、LeetCode47. 全排列 II

一、LeetCode491. 递增子序列

题目链接:491. 递增子序列

题目描述:

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

示例 1:

输入:nums = [4,6,7,7]
输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

示例 2:

输入:nums = [4,4,3,2,1]
输出:[[4,4]]

提示:

  • 1 <= nums.length <= 15
  • -100 <= nums[i] <= 100
算法分析:

这道题看似跟90.子集II一样,但实际解法却有所不同。90.子集II中可以利用排序来去重,但是本题要求自增子序列,所以是不能排序的。

首先本体要求子序列,所以元素是不能重复的,需要用传递参数startIndex来调整下一层递归的起始位置。

其次每层递归当中也有可能出现相同的元素,所以我们要对每层递归的元素进行去重,我们用HashSet。

代码如下:

class Solution {
    List>result =  new ArrayList<>();//收集结果(递增子序列)
    LinkedListpath = new LinkedList<>();//遍历所有递增子序列
    int len;//数组长度
    public void backTravel(int[] nums, int index) {
        if(path.size() >= 2) result.add(new LinkedList<>(path));//收集递增子序列
        HashSet set = new HashSet();//对本层递归的元素进行去重
        for(int i = index; i < len; i++) {
            if((path.size() != 0 && nums[i] < path.getLast()) || set.contains(nums[i])) continue;
            else {
                path.add(nums[i]);
                set.add(nums[i]);//记录这个元素本层已经用过了,后面不能再用
                backTravel(nums, i + 1);//递归
                path.removeLast();//回溯
            }
        }
    }
    public List> findSubsequences(int[] nums) {
        len = nums.length;
        backTravel(nums, 0);
        return result;
    }
}

二、LeetCode46. 全排列

题目链接:46. 全排列
题目描述:

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2:

输入:nums = [0,1]
输出:[[0,1],[1,0]]

示例 3:

输入:nums = [1]
输出:[[1]]

提示:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums 中的所有整数 互不相同

算法分析:

这道题的去重操作要在path中进行。

递归传递参数只需要nums数组即可。

结束条件:path长度等于数组长度时,表明有一种排列方法了,将其放入结果集,然后结束递归。

for循环遍历数组,同时判断当前元素是否在path中出现过,如果没出现将其放入path中,同时标记其已存在path,然后递归,后在回溯(path中删除该元素,同时消除标记)。

代码如下:

class Solution {
    List>result = new ArrayList<>();//收集全排列的结果
    LinkedListpath = new LinkedList<>();//遍历每一种排列
    int len;//数组长度
    boolean[] used = new boolean[21];//记录path中出现的元素,对path进行去重
    public void backTravel(int[] nums) {
        if(path.size() == len) {//如果path的长度等于数组的长度,说明有一种排列组合了,将其放入结果集
            result.add(new LinkedList<>(path));
            return;
        }
        for(int i = 0; i < len; i++) {//从下标0开始横向遍历数组,判断每个元素是否在path中出现过
            if(used[i] == true) continue;
            //如果该元素还没在path中出现,将其放入path同时对其进行标记,表明已在path中
            path.add(nums[i]);
            used[i] = true;
            backTravel(nums);//递归
            //回溯
            path.removeLast();//path中删除最后一个元素
            used[i] = false;//同时消除其标记,表明已从path中去除
        }
    }
    public List> permute(int[] nums) {
        len = nums.length;
        backTravel(nums);
        return result;
    }
}

三、LeetCode47. 全排列 II

题目链接:47. 全排列 II
题目描述:

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

示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],
 [1,2,1],
 [2,1,1]]

示例 2:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

提示:

  • 1 <= nums.length <= 8
  • -10 <= nums[i] <= 10
算法分析:

这道题因数组中包含重复元素,所以要进行两重去重操作。

第一重去重:对path上的元素进行去重(注意是对同一个元素去重,其他值相等的元素不算同一个元素)。

第二重去重:每一层递归当中,对与使用过的元素相等元素进行去重(可以是值相等的不同元素)。

代码如下:

class Solution {
    List>result = new ArrayList<>();//收集结果集
    LinkedListpath = new LinkedList<>();//遍历每一种排序
    int len;//数组长度
    boolean[] used = new boolean[21];//记录每个元素在path上是否出现过,一遍对path去重
    public void backTravel(int[] nums) {
        if(path.size() == len) {//如果path长度等于数组长度,说明找到一种排列,将其放入结果集,结束递归
            result.add(new LinkedList<>(path));
            return;
        }
        HashSetset = new HashSet();//同层递归对使用过的相同元素进行去重(注意这儿的去重是对于值相同的元素,而used是对同一个元素去重)
        for(int i = 0; i < len; i++) {
            //如果当前元素在path存在或者在这一层递归中使用过值相等的元素,直接进行下一层for循环
           if(used[i] == true || set.contains(nums[i])) continue;
            path.add(nums[i]);//将该元素插入path
            used[i] = true;//该元素标记在path中使用过
            set.add(nums[i]);//标记在这一层递归中使用过
            backTravel(nums);//进行下一层递归
            //回溯,
            path.removeLast();//从path中拿出该元素
            used[i] = false;//标记path中没有该元素
        }
    }
    public List> permuteUnique(int[] nums) {
        len = nums.length;
        backTravel(nums);
        return result;
    }
}

总结

要注意区分不同情况下对元素的不同去重操作。

你可能感兴趣的:(Java算法题解,算法,leetcode,数据结构,java)