Leetcode刷题笔记之 回溯

本文深入探讨了计算机科学中的几种经典组合问题,包括无重复元素的子集、有重复元素的子集、组合、组合总和以及全排列等。通过递归和回溯法详细阐述了解决这些问题的算法实现,并提供了示例代码,帮助读者理解如何生成所有可能的解集。同时,还介绍了电话号码字母组合和单词缩写的生成方法,展示了如何在字符串处理中应用这些算法。
摘要由CSDN通过智能技术生成
78. 子集

难度中等1058

给你一个整数数组 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 <= nums[i] <= 10
  • nums 中的所有元素 互不相同
class Solution {
public:
    vector<vector<int>> res;
    vector<int> Nums;
    vector<vector<int>> subsets(vector<int>& nums) {
        Nums = nums;
        vector<int> temp;
        subsets_help(0,temp);
        return res;
    }
    void subsets_help(int j, vector<int> temp){      
        res.push_back(temp);
        for(int i = j; i<Nums.size(); i++){
            temp.push_back(Nums[i]);
            subsets_help(i+1,temp);
            temp.pop_back();
        }       
    }
};
90. 子集 II

难度中等415

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

**说明:**解集不能包含重复的子集。

示例:

输入: [1,2,2]
输出:
[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]
class Solution {
public:
    vector<vector<int>> res;
    vector<int> Nums;
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        Nums = nums;
        vector<int> temp;
        subsetsWithDup_helper(0, temp);
        return res;
    }
    void subsetsWithDup_helper(int j, vector<int> temp){
        res.push_back(temp);
        for(int i = j;i<Nums.size();i++){
            if(i>j && Nums[i]== Nums[i-1]) continue;//这轮的点和上一轮的点重复没关系,但是新加入的点不能和这轮重复
            temp.push_back(Nums[i]);
            subsetsWithDup_helper(i+1,temp);
            temp.pop_back();
        }
    }
};
77. 组合

难度中等525

给定两个整数 nk,返回 1 … n 中所有可能的 k 个数的组合。

示例:

输入: n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]
class Solution {
public:
    int N;
    int K;
    vector<vector<int>> res;
    vector<vector<int>> combine(int n, int k) {
        N = n; K = k;
        vector<int> temp;
        combine_helper(1,temp);
        return res;
    }
    void combine_helper(int j, vector<int> temp){
        if(temp.size()==K) {
            res.push_back(temp);
            return;
        }
        for(int i=j;i<=N-(K-temp.size())+1;i++){//优化剪枝,删去无数可选的情况
            temp.push_back(i);
            combine_helper(i+1,temp);
            temp.pop_back();
        }
    }
};
39. 组合总和

难度中等1220

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。

示例 1:

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

示例 2:

输入:candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]

提示:

  • 1 <= candidates.length <= 30
  • 1 <= candidates[i] <= 200
  • candidate 中的每个元素都是独一无二的。
  • 1 <= target <= 500
class Solution {
public:
    vector<vector<int>> res;
    vector<int> Can;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<int> temp;
        sort(candidates.begin(),candidates.end());
        Can = candidates;
        combinationSum_helper(target, 0,temp);
        return res;
    }
    void combinationSum_helper(int target,int start,vector<int> temp){
        if(target==0){
            res.push_back(temp);
            return;
        }
        if(target<0)return;
        for(int i= start;i< Can.size();i++ ){
            temp.push_back(Can[i]);
            combinationSum_helper(target-Can[i],i,temp);
            temp.pop_back();
        }
    }
};
40. 组合总和 II

难度中等528

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明:

  • 所有数字(包括目标数)都是正整数。
  • 解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
  [1,2,2],
  [5]
]
class Solution {
public:
    vector<vector<int>> res;
    vector<int> Candidates;

    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(),candidates.end());
        Candidates = candidates;
        vector<int> temp;
        combinationSum2_helper(target,0,temp);
        return res;
    }
    void combinationSum2_helper(int target,int start, vector<int> temp){
        if(target==0){
            res.push_back(temp);
            return;
        }
        if(target <0 )return;

        for(int i=start;i<Candidates.size();i++){
            if(i>start && Candidates[i]==Candidates[i-1])continue;
            if(target<Candidates[i]) return; //剪枝!
            temp.push_back(Candidates[i]);
            combinationSum2_helper(target-Candidates[i],i+1,temp);
            temp.pop_back();
        }
    }
};
46. 全排列

难度中等1219

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例:

输入: [1,2,3]
输出:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]
class Solution {
public:
    vector<vector<int>> res;
    vector<int> Nums;
    vector<vector<int>> permute(vector<int>& nums) {
        Nums = nums;
        vector<int> temp;
        vector<bool> used(nums.size(),false);
        permute_helper(0,temp,used);
        return res;
    }
    void permute_helper(int start,vector<int> temp,vector<bool> used){
        if(temp.size()== Nums.size()){
            res.push_back(temp);
            return;
        }
        for(int i = 0;i<Nums.size();i++){
            if(used[i]==true) continue;
            temp.push_back(Nums[i]);
            used[i]=true;
            permute_helper(i+1,temp,used);
            temp.pop_back();
            used[i] = false;
        }
    }
};
47. 全排列 II

难度中等632

给定一个可包含重复数字的序列 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
class Solution {
public:
    vector<int> Nums;
    vector<vector<int>> res;
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        Nums = nums;
        vector<int> temp;
        vector<bool> used(nums.size(),false);
        permuteUnique_helper(temp,used);
        return res;
    }

    void permuteUnique_helper(vector<int> temp,vector<bool> used){
        if(temp.size()== Nums.size()){
            res.push_back(temp);
            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;
            temp.push_back(Nums[i]);
            used[i] = true;
            permuteUnique_helper(temp,used);
            temp.pop_back();
            used[i] = false;
        }
    }
};
17. 电话号码的字母组合

难度中等1190

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dKBBBlkf-1627477236813)(回溯.assets/17_telephone_keypad.png)]

示例 1:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

示例 2:

输入:digits = ""
输出:[]

示例 3:

输入:digits = "2"
输出:["a","b","c"]

提示:

  • 0 <= digits.length <= 4
  • digits[i] 是范围 ['2', '9'] 的一个数字。
class Solution {
public:
    vector<string> letter={"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    string Digits;
    vector<string> res;

    vector<string> letterCombinations(string digits) {
        Digits = digits;
        string temp;
        if(digits.empty()) return res;
        letterCombinations_helper(0,temp);
        return res;
    }

    void letterCombinations_helper(int start, string temp){
        if(temp.size()==Digits.size()){
            res.push_back(temp);
            return;
        }
        for(int i = 0;i<letter[Digits[start]-'2'].size();i++){
            temp.push_back(letter[Digits[start]-'2'][i]);
            letterCombinations_helper(start+1,temp);
            temp.pop_back();
        }
    }
};
320. 列举单词的全部缩写

难度中等52

请你写出一个能够举单词全部缩写的函数。

**注意:**输出的顺序并不重要。

示例:

输入: "word"
输出:
["word", "1ord", "w1rd", "wo1d", "wor1", "2rd", "w2d", "wo2", "1o1d", "1or1", "w1r1", "1o2", "2r1", "3d", "w3", "4"]
class Solution {
public:
    vector<string>res;
    string Word;
    vector<string> generateAbbreviations(string word) {
        Word = word;
        string temp;
        generateAbbreviations_helper(0,0,temp);
        return res;
    }

    void generateAbbreviations_helper(int start, int num, string temp){
        if(start == Word.size()){
            if(num==0) res.push_back(temp);
            else{
                temp+=to_string(num);
                res.push_back(temp);
            }
            return;
        }

        //start字母被缩写
        generateAbbreviations_helper(start+1,num+1,temp);

        //start不被缩写
        int currentLength = temp.size();
        if(num!=0)temp+=to_string(num);
        temp.push_back(Word[start]);
        generateAbbreviations_helper(start+1,0,temp);
        temp.resize(currentLength);
    }
};
254. 因子的组合

难度中等71

整数可以被看作是其因子的乘积。

例如:

8 = 2 x 2 x 2;
  = 2 x 4.

请实现一个函数,该函数接收一个整数 n 并返回该整数所有的因子组合。

注意:

  1. 你可以假定 n 为永远为正数。
  2. 因子必须大于 1 并且小于 n

示例 1:

输入: 1
输出: []

示例 2:

输入: 37
输出: []

示例 3:

输入: 12
输出:
[
  [2, 6],
  [2, 2, 3],
  [3, 4]
]

示例 4:

输入: 32
输出:
[
  [2, 16],
  [2, 2, 8],
  [2, 2, 2, 4],
  [2, 2, 2, 2, 2],
  [2, 4, 4],
  [4, 8]
]
class Solution {
public:

    vector<vector<int>> res;
    vector<vector<int>> getFactors(int n) {
        vector<int> temp;
        getFactors_helper(n,2,temp);       
        return res;
    }

    void getFactors_helper(int target,int start,vector<int> temp){

        if(target==1){
            if(temp.size()>1)res.push_back(temp);
        }
        for(int i=start;i*i<=target;i++){
            if(target%i!=0 ) continue;
            //这轮加入
            temp.push_back(i);
            temp.push_back(target/i);
            res.push_back(temp);
            //继续分解
            temp.pop_back();
            getFactors_helper(target/i,i,temp);
            temp.pop_back();
        }
    }
};
剑指 Offer 38. 字符串的排列

难度中等243

输入一个字符串,打印出该字符串中字符的所有排列。

你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

示例:

输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]

1.生成法(没有剪枝,使用set在输出时去重)

class Solution {
public:
    unordered_set<string> res;
    int strSize;
    string S;
    vector<string> permutation(string s) {
        string temp;
        strSize = s.size();
        S = s;
        vector<bool> used(strSize,false);
        permutation_help(used,temp,s.size());
        vector<string> vecRes;
        for(auto i:res){
            vecRes.push_back(i);
        }
        return vecRes;
    }

    void permutation_help(vector<bool>used, string temp,int remain){
        if(remain==0)
        res.emplace(temp);

        for(int i=0;i<strSize;i++){
            if(used[i])continue;
            used[i] = true;
            temp += S[i];
            permutation_help(used, temp,remain-1);
            temp.pop_back();
            used[i] = false;
        }
    }
};

2.交换法(有剪枝)

class Solution {
public:
    vector<string> permutation(string s) {
        dfs(s, 0);
        return res;
    }
private:
    vector<string> res;
    void dfs(string s, int x) {
        if(x == s.size() - 1) {
            res.push_back(s);                       // 添加排列方案
            return;
        }
        set<int> st;
        for(int i = x; i < s.size(); i++) {
            if(st.find(s[i]) != st.end()) continue; // 重复,因此剪枝
            st.insert(s[i]);
            swap(s[i], s[x]);                       // 交换,将 s[i] 固定在第 x 位
            dfs(s, x + 1);                          // 开启固定第 x + 1 位字符
            swap(s[i], s[x]);                       // 恢复交换
        }
    }
};

法(有剪枝)

class Solution {
public:
    vector<string> permutation(string s) {
        dfs(s, 0);
        return res;
    }
private:
    vector<string> res;
    void dfs(string s, int x) {
        if(x == s.size() - 1) {
            res.push_back(s);                       // 添加排列方案
            return;
        }
        set<int> st;
        for(int i = x; i < s.size(); i++) {
            if(st.find(s[i]) != st.end()) continue; // 重复,因此剪枝
            st.insert(s[i]);
            swap(s[i], s[x]);                       // 交换,将 s[i] 固定在第 x 位
            dfs(s, x + 1);                          // 开启固定第 x + 1 位字符
            swap(s[i], s[x]);                       // 恢复交换
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1 目标检测的定义 目标检测(Object Detection)的任务是找出图像中所有感兴趣的目标(物体),确定它们的类别和位置,是计算机视觉领域的核心问题之一。由于各类物体有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具有挑战性的问题。 目标检测任务可分为两个关键的子任务,目标定位和目标分类。首先检测图像中目标的位置(目标定位),然后给出每个目标的具体类别(目标分类)。输出结果是一个边界框(称为Bounding-box,一般形式为(x1,y1,x2,y2),表示框的左上角坐标和右下角坐标),一个置信度分数(Confidence Score),表示边界框中是否包含检测对象的概率和各个类别的概率(首先得到类别概率,经过Softmax可得到类别标签)。 1.1 Two stage方法 目前主流的基于深度学习的目标检测算法主要分为两类:Two stage和One stage。Two stage方法将目标检测过程分为两个阶段。第一个阶段是 Region Proposal 生成阶段,主要用于生成潜在的目标候选框(Bounding-box proposals)。这个阶段通常使用卷积神经网络(CNN)从输入图像中提取特征,然后通过一些技巧(如选择性搜索)来生成候选框。第二个阶段是分类和位置精修阶段,将第一个阶段生成的候选框输入到另一个 CNN 中进行分类,并根据分类结果对候选框的位置进行微调。Two stage 方法的优点是准确度较高,缺点是速度相对较慢。 常见Tow stage目标检测算法有:R-CNN系列、SPPNet等。 1.2 One stage方法 One stage方法直接利用模型提取特征值,并利用这些特征值进行目标的分类和定位,不需要生成Region Proposal。这种方法的优点是速度快,因为省略了Region Proposal生成的过程。One stage方法的缺点是准确度相对较低,因为它没有对潜在的目标进行预先筛选。 常见的One stage目标检测算法有:YOLO系列、SSD系列和RetinaNet等。 2 常见名词解释 2.1 NMS(Non-Maximum Suppression) 目标检测模型一般会给出目标的多个预测边界框,对成百上千的预测边界框都进行调整肯定是不可行的,需要对这些结果先进行一个大体的挑选。NMS称为非极大值抑制,作用是从众多预测边界框中挑选出最具代表性的结果,这样可以加快算法效率,其主要流程如下: 设定一个置信度分数阈值,将置信度分数小于阈值的直接过滤掉 将剩下框的置信度分数从大到小排序,选中值最大的框 遍历其余的框,如果和当前框的重叠面积(IOU)大于设定的阈值(一般为0.7),就将框删除(超过设定阈值,认为两个框的里面的物体属于同一个类别) 从未处理的框中继续选一个置信度分数最大的,重复上述过程,直至所有框处理完毕 2.2 IoU(Intersection over Union) 定义了两个边界框的重叠度,当预测边界框和真实边界框差异很小时,或重叠度很大时,表示模型产生的预测边界框很准确。边界框A、B的IOU计算公式为: 2.3 mAP(mean Average Precision) mAP即均值平均精度,是评估目标检测模型效果的最重要指标,这个值介于0到1之间,且越大越好。mAP是AP(Average Precision)的平均值,那么首先需要了解AP的概念。想要了解AP的概念,还要首先了解目标检测中Precision和Recall的概念。 首先我们设置置信度阈值(Confidence Threshold)和IoU阈值(一般设置为0.5,也会衡量0.75以及0.9的mAP值): 当一个预测边界框被认为是True Positive(TP)时,需要同时满足下面三个条件: Confidence Score > Confidence Threshold 预测类别匹配真实值(Ground truth)的类别 预测边界框的IoU大于设定的IoU阈值 不满足条件2或条件3,则认为是False Positive(FP)。当对应同一个真值有多个预测结果时,只有最高置信度分数的预测结果被认为是True Positive,其余被认为是False Positive。 Precision和Recall的概念如下图所示: Precision表示TP与预测边界框数量的比值 Recall表示TP与真实边界框数量的比值 改变不同的置信度阈值,可以获得多组Precision和Recall,Recall放X轴,Precision放Y轴,可以画出一个Precision-Recall曲线,简称P-R
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值