LeetCode Day31

90 阅读2分钟

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

思路

  1. 定义数据结构:
    • result:用于存储所有找到的递增子序列。
    • current:用于存储当前正在构建的递增子序列。
  2. 回溯函数:该函数应从数组的某个位置开始,在回溯的每一步中做出选择(添加一个数到当前递增子序列或不添加),然后进入下一层递归。
  3. 结束条件:如果当前的递增子序列长度大于等于2,将其添加到结果中。
  4. 去重:在同一递归层次中,如果一个数已经被选择过,那么就跳过这个数,以避免产生重复的子序列。

题解

class Solution {
private:
    vector<vector<int>> result;
    vector<int> current;

public:
    void backtracking(vector<int>& nums, int start) {
        if (current.size() >= 2) {
            result.push_back(current);
        }
        unordered_set<int> seen;
        for (int i = start; i < nums.size(); ++i) {
            if ((current.empty() || nums[i] >= current.back()) && seen.find(nums[i]) == seen.end()) {
                current.push_back(nums[i]);  // 选择
                backtracking(nums, i + 1);   // 回溯
                current.pop_back();          // 撤销选择
                seen.insert(nums[i]);        // 标记为已选择
            }
        }
    }

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

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 中的所有整数 互不相同

思路

在排列问题中,每次递归调用都可以从整个数组中选择元素,而不仅仅是从某个起始索引开始。这是因为排列问题需要考虑数组中所有元素的所有可能顺序,而不是仅考虑从某个点开始的子数组。

与组合问题不同,在组合问题中,我们通常使用 startIndex 来避免生成重复的组合,因为组合 [1, 2][2, 1] 被认为是相同的。但在排列问题中,[1, 2][2, 1] 是两个不同的排列。

因此,在排列问题中,我们通常使用一个额外的数据结构(如集合或布尔数组)来跟踪哪些元素已经被添加到当前排列中,而不是通过 startIndex 来限制可选元素的范围。

题解

class Solution {
private:
    vector<vector<int>> result;
    vector<int> current;

public:
    void backtracking(vector<int>& nums, unordered_set<int>& seen) {
        if (current.size() == nums.size()) {
            result.push_back(current);
            return;
        }
        for (int i = 0; i < nums.size(); ++i) {
            if (seen.find(nums[i]) == seen.end()) {
                current.push_back(nums[i]);  // 选择
                seen.insert(nums[i]);        // 标记为已选择
                backtracking(nums, seen);    // 回溯
                seen.erase(nums[i]);         // 取消标记
                current.pop_back();          // 撤销选择
            }
        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        unordered_set<int> seen;
        backtracking(nums, seen);
        return result;
    }
};

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

思路

  1. 排序数组:首先,对数组进行排序。这样,所有的相同数字都会相邻出现,更容易进行去重操作。
  2. 使用额外数据结构:使用一个布尔数组 used 来跟踪哪些数字已经被添加到当前排列中。
  3. 去重:在同一递归层次中,如果一个数和它前面的数相同,并且前面的数已经被使用过,那么就跳过这个数。

题解

class Solution {
private:
    vector<vector<int>> result;
    vector<int> current;

public:
    void backtracking(vector<int>& nums, vector<bool>& used) {
        if (current.size() == nums.size()) {
            result.push_back(current);
            return;
        }
        for (int i = 0; i < nums.size(); ++i) {
            if (used[i]) continue;
            // 去重:跳过在同一递归层次中相同的元素
            if (i > 0 && nums[i] == nums[i-1] && !used[i-1]) continue;
            current.push_back(nums[i]);  // 选择
            used[i] = true;              // 标记为已选择
            backtracking(nums, used);    // 回溯
            used[i] = false;             // 取消标记
            current.pop_back();          // 撤销选择
        }
    }

    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());  // 排序
        vector<bool> used(nums.size(), false);
        backtracking(nums, used);
        return result;
    }
};