代码随想录算法训练营第29天|491.递增子序列,46.全排列,47.全排列II

文章介绍了如何使用回溯算法解决491.递增子序列、46.全排列和47.全排列II这三道编程题目。回溯法在每一步选择中都尽可能深地进入搜索树,并在达到预设条件时返回。对于全排列问题,关键在于处理排列的顺序和去重策略。

代码随想录算法训练营第29天|491.递增子序列,46.全排列,47.全排列II

491.递增子序列

题目链接:491.递增子序列,难度:中等
【实现代码】

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backTracking(vector<int>& nums, int startIndex) {
        if (path.size() >= 2) {
            result.push_back(path);
        }
        
        int used[201] = {0};
        for (int i = startIndex; i < nums.size(); i++) {
            // cout << i << " ";            
            if ((!path.empty() && nums[i] < path.back()) || used[nums[i] + 100] == 1) {
                continue;
            }
            path.push_back(nums[i]);
            used[nums[i] + 100] = 1;
            backTracking(nums, i + 1);
            path.pop_back();
        }
        // cout << endl;
    }
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        backTracking(nums, 0);
        return result;
    }
};

【解题思路】

子集问题使用回溯算法解决。
回溯三步曲:

  1. 确定函数参数:原数组和开始索引
  2. 确定结束条件:当path的长度大于2时就结束
  3. 单层逻辑:当前元素大于数组尾值时,且没有使用过就存入path,然后就行下一个递归。本题目的去重是树层去重。

46.全排列

题目链接:46.全排列,难度:中等
【实现代码】

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
   
    void backTracking(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 (false == used[i]) {
                path.push_back(nums[i]);
                used[i] = true;
                backTracking(nums, used);
                path.pop_back();
                used[i] = 0;
            }            
        }
    }
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<bool> used(nums.size(), false);
        backTracking(nums, used);
        return result;
    }
};

【解题思路】

排列问题使用回溯算法解决。
回溯三步曲:

  1. 确定函数参数:原数组和used数组,因为排列有顺序区分,所以每一层都是从0开始循环
  2. 确定结束条件:当path的长度等于原数组的长度,就结束
  3. 单层逻辑:如果当前元素没有使用过就存入path,然后进行下一个递归。

47.全排列II

题目链接:47.全排列II,难度:中等
【实现代码】

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backTracking(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 ((i > 0 && nums[i] == nums[i - 1]) && false == used[i - 1]) {
                continue;
            }
            if (true == used[i]) {
                continue;
            }
            // cout << i << " ";
            path.push_back(nums[i]);
            used[i] = true;
            backTracking(nums, used);
            path.pop_back();
            used[i] = false;
        }
        // cout << endl;
    }
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {  
        sort(nums.begin(), nums.end());
        vector<bool> used(nums.size(), false);
        backTracking(nums, used);
        return result;
    }
};

【解题思路】

本题目和上一题类似,难点在于去重。
回溯三步曲:

  1. 确定函数参数:原数组和used数组
  2. 确定结束条件:当path数组的长度等于原数组的长度时就结束
  3. 单层逻辑:首先就是树层去重,当前元素和前一个元素相等时,used对应下标的值为false,则前一个元素已经使用过,跳过当前元素;接着是跳过已经使用的元素,即在一个树枝中,used对应下标的值为true,即使用过,最后存入path,进行下一个递归。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值