LeetCode|回溯算法|491. 递增子序列 、46. 全排列、47. 全排列 II

目录

一、491. 递增子序列

1.题目描述

2.解题思路

3.代码实现

二、46. 全排列

1.题目描述

2.解题思路

3.代码实现

三、47. 全排列 II

1.题目描述

2.解题思路

3.代码实现


 

一、491. 递增子序列

1.题目描述

给你一个整数数组 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]]

2.解题思路

1.题目要求递增的子序列,因此在每次递归backtracking的时候,需要判断第一个元素和path路径中的最后一个元素大小关系(只有大于等于path.back() 才会push进去)。

  • 这里要注意,和path.back()比较时,需要先对path做非空判断。如果path是空的,说明当前元素是第一个元素,直接push即可。

 2.这里收集结果的条件是,只要path数组中的元素大于1(也就是题目要求的至少有两个元素)。

3.去重操作

①利用unordered set数组来做去重,如果当前元素在本层中已经出现过,直接continue

②直接利用数组做去重操作,对数组初始化0,使用过的元素就+1。

 

3.代码实现

class Solution {
public:
    vector<int> path;//收集路径元素
    vector<vector<int>> result;//结果集
    //确定函数参数
    void backtracking(const vector<int>& nums,int startIndex){
        //只要path中存在了两个及以上的元素,就收集结果
        if(path.size() > 1){
            result.push_back(path);
        }

        //确定终止条件
        if(startIndex >= nums.size()){
            //result.push_back(path);
            return;
        }
        //单层逻辑
        //使用unordered set对本层做去重操作
        unordered_set<int> uset;
        for(int i = startIndex;i < nums.size();i++){
            //先判断是否递增,如果path是空,说明当前是第一个元素,无需判断递增关系
            //还要判断:如果这个元素在本层使用过,就去重
            if(!path.empty() && nums[i] < path.back() || uset.find(nums[i]) != uset.end()){
                continue;
            }
            //收集路径
            path.push_back(nums[i]);
            uset.insert(nums[i]);
            //深度递归
            backtracking(nums,i + 1);
            //回溯操作
            path.pop_back();
        }
    }

    vector<vector<int>> findSubsequences(vector<int>& nums) {
        result.clear();
        path.clear();
        backtracking(nums,0);
        return result;
    }
};

二、46. 全排列

1.题目描述

给定一个不含重复数字的数组 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]]

2.解题思路

        排列问题和组合问题的主要区别就是:举个例子,排列中[1,2] 和 [2,1]是两中结果,而在组合中这就是一种结果。

关键点一:如何确定终止条件?

  • 当path数组中的元素个数和nums数组个数一样时,便是收集结果的时候。

关键点二:如何确定单层递归逻辑?

  • 这里因为是排列问题,每次for循环的i不是从startIndex开始,而是从0开始
  • 使用used数组来反映当前元素是否使用过。 

3.代码实现

class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;//结果集
    //确定函数参数
    void backtracking(const vector<int>& nums,vector<bool>& used){
        //确定终止条件
        if(path.size() == nums.size()){
            result.push_back(path);
            return;
        }
        //单层逻辑
        for(int i = 0;i < nums.size();i++){
            if(used[i] == false){
                path.push_back(nums[i]);
                used[i] = true;
                //深度递归
                backtracking(nums,used);
                //回溯操作
                path.pop_back();
                used[i] = false;
            }
            else{
                continue;
            }
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        path.clear();
        result.clear();
        vector<bool> used(nums.size(),false);//定义一个长度和nums一样的数组used,用来判断元素是否已经使用过
        backtracking(nums,used);
        return result;
    }
};

三、47. 全排列 II

1.题目描述

给定一个可包含重复数字的序列 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]]

2.解题思路

  • 去重的核心和组合问题一样,先对题目所给数组nums排序,让相邻元素邻近。
  • 判断nums[i] 和nums[ i - 1]是否相同(注意:这里判断相同,一定是同一树层的相同才continue,同一树枝的不用continue)

3.代码实现

class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;//结果集
    //确定函数参数
    void backtracking(const vector<int>& nums,vector<bool>& used){
        //确定终止条件
        if(path.size() == nums.size()){
            result.push_back(path);
            return;
        }
        //单层逻辑
        for(int i = 0;i < nums.size();i++){
            //先判断当前元素是否使用过
            //还需要判断同一树层,当前元素是否和前一个元素一样,如果一样,直接跳过
            //used[i - 1] == flase表示在同一树层使用过
            //used[i - 1] == true 表示在同一树枝使用过
            if(i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false){
                continue;
            }
            if(used[i] == false){//没有被使用过
                path.push_back(nums[i]);
                used[i] = true;
                //深度递归
                backtracking(nums,used);
                //回溯操作
                path.pop_back();
                used[i] = false;
            }
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        //对nums排序,相邻元素放一块,方便后续去重
        sort(nums.begin(),nums.end());
        vector<bool> used(nums.size(),false);
        backtracking(nums,used);
        return result;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值