代码随想录-回溯算法(子集问题)|ACM模式

目录

前言:

[78. 子集](#78. 子集)

题目描述:

输入输出描述:

思路和想法:

[90. 子集 II](#90. 子集 II)

题目描述:

输入输出描述:

思路和想法:

[491. 递增子序列](#491. 递增子序列)

题目描述:

输入输出描述:

思路和想法:


前言:

如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!

子集是无序的,取过的元素不会重复取,写回溯算法的时候,for就要从Index开始。

之后讨论的问题,分为两种:

  • 集合里无重复元素
  • 集合里有重复元素,求取的子集需要进行去重(可排序)
  • 集合中找递增子集(不可排序)

78. 子集

题目描述:

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

输入输出描述:

示例1:

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

示例2:

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

提示:

  • 1 <= nums.length <= 10
  • -10 <= numsi <= 10
  • nums 中的所有元素 互不相同

思路和想法:

这一道题是模板题,可以从这道题感受子集问题解法与之前的不同。

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;
/*
* 作者:希希雾里
* 78. 子集
* */
vector<vector<int>> result;
vector<int> path;

/*
* 函数:          backtracing()
* 输入参数:       s:输入的字符串    index:数组元素下标
* */
void backtracing(vector<int> &nums,int Index){
    result.push_back(path);
    //回溯过程
    for (int i = Index; i < nums.size(); ++i) {
        path.push_back(nums[i]);
        backtracing(nums,i + 1);
        path.pop_back();
    }
};


int main() {
    result.clear();
    path.clear();

    int num;
    vector<int> nums;
    while(cin>>num){
        nums.push_back(num);
        if(getchar() == '\n'){
            break;
        }
    }

    backtracing(nums,0);
    cout << "[";
    for (int i = 0; i < result.size(); ++i) {
        cout << "[";
        for (int j = 0; j < result[i].size(); ++j) {
            cout<< result[i][j];
            if(j != result[i].size() - 1) cout << ",";
        }
        cout << "]";
        if(i != result.size() - 1)  {cout << ",";}
    }
    cout << "]";
    return 0;
}

/*  测试样例
1 2 3

0
*
* */

90. 子集 II

题目描述:

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

输入输出描述:

示例1:

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

示例2:

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

提示:

  • 1 <= nums.length <= 10
  • -10 <= numsi <= 10

思路和想法:

这道题目和上一道题之间的区别:集合里有了重复元素,而且求取的子集要进行去重。

所以在上一道题的基础上,引入树层去重,这道题目可以参考回溯算法中组合问题里的40.组合总和II。

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;
/*
* 作者:希希雾里
* 90. 子集II
* */
vector<vector<int>> result;
vector<int> path;

/*
* 函数:          backtracing()
* 输入参数:       s:输入的字符串    index:数组元素下标  used:使用过的标志
* */
void backtracing(vector<int> &nums,int Index, vector<bool> used){
    result.push_back(path);
    //回溯过程
    for (int i = Index; i < nums.size(); ++i) {
        // used[i - 1] == true,说明同一树枝nums[i - 1]使用过
        // used[i - 1] == false,说明同一树层nums[i - 1]使用过
        // 这里注意树层和树枝的区别,这里我们是对树层进行跳过。
        if(i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false){
            continue;
        }
        path.push_back(nums[i]);
        used[i] = true;
        backtracing(nums,i + 1,used);
        used[i] = false;
        path.pop_back();
    }
};


int main() {
    result.clear();
    path.clear();

    int num;
    vector<int> nums;
    while(cin>>num){
        nums.push_back(num);
        if(getchar() == '\n'){
            break;
        }
    }
    vector<bool> used(nums.size(), false);
    sort(nums.begin(),nums.end());
    backtracing(nums,0,used);

    //输出模式
    cout << "[";
    for (int i = 0; i < result.size(); ++i) {
        cout << "[";
        for (int j = 0; j < result[i].size(); ++j) {
            cout<< result[i][j];
            if(j != result[i].size() - 1) cout << ",";
        }
        cout << "]";
        if(i != result.size() - 1)  {cout << ",";}
    }
    cout << "]";
    return 0;
}

/*  测试样例
1 2 2

0
*
* */

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 <= numsi <= 100

思路和想法:

这道题目相较于上一道题子集II有什么区别,这道题是要在数组中找到不同的递增子序列,所以不能对原数组进行排序。

所以去重的方法,不能用原来的逻辑,这道题目还是进行树层去重,这里采用set来实现。

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;
/*
* 作者:希希雾里
* 491. 递增子序列
* */
vector<vector<int>> result;
vector<int> path;

/*
* 函数:          backtracing()
* 输入参数:       s:输入的字符串    index:数组元素下标
* */
void backtracing(vector<int> &nums,int Index){
    if(path.size() > 1){
        result.push_back(path);
    }
    //回溯过程
    unordered_set<int> uset;    //使用set对本层元素进行去重,新的一层会重新定义清空的
    for (int i = Index; i < nums.size(); ++i) {
        if((!path.empty() && nums[i] < path.back()) || uset.find(nums[i]) != uset.end()){
            continue;
        }

        uset.insert(nums[i]);
        path.push_back(nums[i]);
        backtracing(nums,i + 1);
        path.pop_back();
    }
};


int main() {
    result.clear();
    path.clear();

    int num;
    vector<int> nums;
    while(cin>>num){
        nums.push_back(num);
        if(getchar() == '\n'){
            break;
        }
    }

    backtracing(nums,0);

    //输出模式
    cout << "[";
    for (int i = 0; i < result.size(); ++i) {
        cout << "[";
        for (int j = 0; j < result[i].size(); ++j) {
            cout<< result[i][j];
            if(j != result[i].size() - 1) cout << ",";
        }
        cout << "]";
        if(i != result.size() - 1)  {cout << ",";}
    }
    cout << "]";
    return 0;
}

/*  测试样例
4 6 7 7

4 4 3 2 1
*
* */
相关推荐
say_fall7 小时前
【Linux系统编程】Ext 文件系统:磁盘与存储、文件与目录、现代计算机究竟如何寻找文件
linux·运维·服务器·数据库·c++
雪落漂泊7 小时前
C++算法 滑动窗口篇(下)
开发语言·c++
夜不会漫长7 小时前
C++:红黑树
数据结构·算法
文丑颜不良啊7 小时前
LeetCode1661~1680题目解析
java·leetcode
AI备案指南-满满8 小时前
算法备案审核不通过,一般是什么原因?
java·人工智能·算法·大模型备案·算法备案·大模型登记
Ramble_Naylor8 小时前
Rust深度解读:Send 与 Sync 的本质与线程安全边界
python·算法·rust
秋天的怀念_David8 小时前
算法一千零一夜 · 第二十三夜 电力民生写成的曲线
java·程序人生·算法
不一样的故事1268 小时前
NB/T 47013.8-2015 附录D 吸枪技术
人工智能·经验分享·算法
通问AI8 小时前
用 200 行 Python 给文档库做「上下文完整度」自检:四个可复算的检查项
算法
揽星河..9 小时前
【C语言】扫雷游戏
c语言·算法·游戏