当前位置: 首页 > article >正文

leetcode每日一题35

90. 子集 II

回溯嘛
子集啊排列组合啊棋盘啊都是回溯
回溯三部曲走起
跟78.子集比,本题给出的数组里存在重复元素了
所以在取元素时,如果同一层里取过某个元素,那么在该层就不能取重复的该元素了
如给出的数组[1,2,2]
可以在某一次递归中第一个取2放进子集,但后面的递归就不允许第一个取2放进子集里了
详情可以看代码随想录的图
代码随想录
所以要有一个数组used记录该层里取过的数

  1. 递归函数参数
    回溯问题一般涉及两个全局变量:
    保存本次递归中符合条件的结果path
    保存所有符合条件的结果的集合result
    以及回溯函数backtracking,因为是求子集问题,所以取过的元素不能重复取,所以回溯时,for循环要从startIndex开始,而不是从0开始
vector<int> path;
vector<vector<int>> result;
void backtracking(vector<int>& nums, int startIndex, vector<bool>& used)
  1. 递归终止条件
    当此时的startIndex已经大于数组长度时,就没有没取过的数组元素了,本次递归就终止了
if(startIndex>=nums.size()){
	return;
}
  1. 单层搜索逻辑
    单层的搜索逻辑是
    先将取出来的数存入path,再递归调用自身,然后回溯,删掉刚才取出来的数
path.push_back(nums[i]);
backtracking(……);
path.pop_back();

本题中,要判断取的nums[i]有没有使用过
如果没有,那么在backtracking要传入used数组,所以要递归前标记nums[i]已经被使用过了而递归后,需要回溯,从path中删除nums[i],所以要恢复为nums[i]未被使用

if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
                continue;
}//判定nums[i]有没有使用过
path.push_back(nums[i]);
used[i]=true;
backtracking(nums, i+1,used);
used[i]=false;
path.pop_back();

所以,回溯算法模板为

void backtracking(参数) {
		收集子集result.push_back(path);
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

那么组合起来,本题的回溯函数为

vector<int> path;
vector<vector<int>> result;
void backtracking(vector<int>& nums, int startIndex, vector<bool>& used){
	result.push_back(path);//收集子集
	if(startIndex>=nums.size()){
		return;
	}
	for(int i =startIndex;i<nums.size();i++)
	{
		if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
	                continue;
		}//判定nums[i]有没有使用过
		path.push_back(nums[i]);
		used[i]=true;
		backtracking(nums, i+1,used);
		used[i]=false;
		path.pop_back();
	}
}
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        result.clear();
        path.clear();
        vector<bool> used(nums.size(), false);
        sort(nums.begin(), nums.end()); // 去重需要排序
        backtracking(nums, 0, used);
        return result;
    }

整理一下,得到最终代码:

class Solution {
private:
    vector<int> path;
    vector<vector<int>> result;
    void backtracking(vector<int>& nums, int startIndex, vector<bool>& used){
        result.push_back(path);//收集子集,要放在判定停止条件前,防止漏数
        if(startIndex>=nums.size()){
            return;
        }
        for(int i =startIndex;i<nums.size();i++)
        {
            if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
                        continue;
            }//判定nums[i]有没有使用过
            path.push_back(nums[i]);
            used[i]=true;
            backtracking(nums, i+1,used);
            used[i]=false;
            path.pop_back();
        }
    }
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        result.clear();
        path.clear();
        vector<bool> used(nums.size(), false);
        sort(nums.begin(), nums.end()); // 去重需要排序
        backtracking(nums, 0, used);
        return result;
    }
};

http://www.kler.cn/a/149150.html

相关文章:

  • 软件测试面试题(800道)【附带答案】持续更新...
  • 编译ffmpeg动态库时设置RPATH为$ORIGIN
  • MySQL_第13章_视图
  • Python标准库模块的使用:math、datetime
  • dapp获取钱包地址,及签名
  • 十九:Spring Boot 依赖(4)-- spring-boot-starter-security依赖详解
  • Web学习笔记
  • 面试必须要知道的MySQL知识--索引
  • AntDB数据库:从海量数据处理,到5G计费商用核心
  • 使用vue-admin-template时,需要注意的问题,包括一定要去除mock.js注释
  • 0005Java程序设计-ssm基于微信小程序的校园求职系统
  • Java后端开发——MVC商品管理程序
  • Java Web基础教程
  • 第二证券:燃料电池产业进入发展快车道 多家公司披露布局进展
  • 【FGPA】Verilog:JK 触发器 | D 触发器 | T 触发器 | D 触发器的实现
  • 【MATLAB】RLMD分解+FFT+HHT组合算法
  • NOI / 1.7编程基础之字符串 30:字符环 c语言 两次遍历解决循环
  • 11.27二叉查找树,遍历二叉树,层序(判断是不是完全二叉树),根据遍历序列重构二叉树,递归输入建树(树的定义,结构体细节,typedef)
  • Spring Boot进行单元测试,一个思路解决重启低效难题!
  • Docker compose启动服务遇到的问题
  • el-tabel实现拖拽排序
  • 绩效考核的基础及基本内容
  • NOI / 1.8编程基础之多维数组 提问24:蛇形填充数组 c语言
  • 进程和线程的关系
  • 力扣106. 从中序与后序遍历序列构造二叉树
  • linux(2)之buildroot使用手册