leetcode刷题记录总结-7.递归回溯算法(进行中)

news2024/7/11 13:13:43

文章目录

  • 零、回溯算法理论
    • 总览
    • 什么是回溯法
    • 回溯法的效率
    • 回溯法解决的问题
    • 如何理解回溯法
    • 回溯法模板
  • 一、组合问题
    • [77. 组合](https://leetcode.cn/problems/combinations/)
      • 题解
        • 递归实现组合型枚举:每个点选与不选
          • 子集问题模板
          • 组合问题解决思路
        • 回溯思路:遍历整棵树-找到所有组合的叶子节点
    • [17.电话号码的字母组合 ](https://leetcode.cn/problems/letter-combinations-of-a-phone-number/)
      • 题解
        • 搜索解题思路
        • 字母组合-子集问题
    • [39. 组合总和 ](https://leetcode.cn/problems/combination-sum/)
      • 题解
    • [40. 组合总和 II ](https://leetcode.cn/problems/combination-sum-ii/)
      • 题解
    • [216. 组合总和 III ](https://leetcode.cn/problems/combination-sum-iii/description/)
      • 题解
        • 递归枚举
        • 递归回溯
  • 二、切割问题
  • 三、子集问题
  • 四、排列问题
  • 五、棋盘问题

零、回溯算法理论

总览

回溯算法大纲

什么是回溯法

回溯法也可以叫做回溯搜索法,它是一种搜索的方式。

在二叉树系列中,我们已经不止一次,提到了回溯,例如二叉树:以为使用了递归,其实还隐藏着回溯 (opens new window)。

回溯是递归的副产品,只要有递归就会有回溯。

回溯函数也就是递归函数,指的都是一个函数

回溯法的效率

回溯法并不是什么高效的算法。

因为回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质。

那么既然回溯法并不高效为什么还要用它呢?

因为没得选,一些问题能暴力搜出来就不错了,撑死了再剪枝一下,还没有更高效的解法。

回溯法解决的问题

回溯法,一般可以解决如下几种问题:

  • 组合问题:N个数里面按一定规则找出k个数的集合
  • 切割问题:一个字符串按一定规则有几种切割方式
  • 子集问题:一个N个数的集合里有多少符合条件的子集
  • 排列问题:N个数按一定规则全排列,有几种排列方式
  • 棋盘问题:N皇后,解数独等等

组合是不强调元素顺序的,排列是强调元素顺序

例如:{1, 2} 和 {2, 1} 在组合上,就是一个集合,因为不强调顺序,而要是排列的话,{1, 2} 和 {2, 1} 就是两个集合了。

记住组合无序,排列有序,就可以了。

如何理解回溯法

回溯法解决的问题都可以抽象为树形结构

因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度,都构成的树的深度

递归就要有终止条件,所以必然是一棵高度有限的树(N叉树)。

回溯法模板

这里给出Carl总结的回溯算法模板。

在讲二叉树的递归 (opens new window)中我们说了递归三部曲,这里我再给大家列出回溯三部曲。

  • 回溯函数模板返回值以及参数

回溯算法中函数返回值一般为void。

再来看一下参数,因为回溯算法需要的参数可不像二叉树递归的时候那么容易一次性确定下来,所以一般是先写逻辑,然后需要什么参数,就填什么参数

回溯函数伪代码如下:

void backtracking(参数)
  • 回溯函数终止条件

既然是树形结构,树中就可以看出,一般来说搜到叶子节点了,也就找到了满足条件的一条答案,把这个答案存放起来,并结束本层递归。

所以回溯函数终止条件伪代码如下:

if (终止条件) {
    存放结果;
    return;
}
  • 回溯搜索的遍历过程

回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成的树的深度。

如图:

回溯算法理论基础

注意图中,我特意举例集合大小和孩子的数量是相等的!

回溯函数遍历过程伪代码如下:

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

for循环就是遍历集合区间,可以理解一个节点有多少个孩子,这个for循环就执行多少次。

backtracking这里自己调用自己,实现递归。

for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了。

分析完过程,回溯算法模板框架如下:

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

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

一、组合问题

77. 组合

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

你可以按 任何顺序 返回答案。

示例 1:

输入:n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

示例 2:

输入:n = 1, k = 1
输出:[[1]]

提示:

  • 1 <= n <= 20
  • 1 <= k <= n

题解

递归实现组合型枚举:每个点选与不选

实际上求组合问题与求子集问题类似,均使用dfs,单层递归逻辑都是递归处理每个位置选或是不选的问题,为节约空间,用一个临时全局数组保存每次选择的结果,最后汇总,就是子集或是组合的问题。

子集问题模板
  • 1.相同的逻辑单元:每个位置的元素,选或者不选,然后进入下个位置的判断
  • 2.边界条件:所有位置都验证完毕,返回
  • 3.每个分支需要用数组储存选择的结果,用全局变量,免得每个分支开销
    • 每个分支使用后,要对称的清空数组元素
    • 保证下个分支使用前,数组是干净的
vector<int> temp;
void dfs(int cur, int n) {
    if (cur == n + 1) {
        // 记录答案
        // ...
        return;
    }
    // 考虑选择当前位置
    temp.push_back(cur);
    dfs(cur + 1, n, k);
    temp.pop_back();
    // 考虑不选择当前位置
    dfs(cur + 1, n, k);
}
组合问题解决思路
  • 1.与求子集问题类似,也是递归判断每个位置选或不选的分支问题
  • 2.本题有k个元素的限制,因此,可以进行剪枝处理
    • 即,如果子集个数已经大于k,当前分支无需继续遍历判断
    • 如果后面所有元素的个数全部都选,都不够k,当前分支也舍去
class Solution {
public:
    vector<vector<int>> ans;
    vector<int> opt;    // 临时数组记录每次选择结果
    vector<vector<int>> combine(int n, int k) {
        dfs(n, k, 1); // 题中从1遍历到n,走了n+1步
        return ans;
    }
    void dfs(int n, int k, int startIndex) {
        // 1. 剪纸:数量超过k,或后面元素全选也不够k,一定不满足
        if(opt.size() > k || opt.size() + n - startIndex + 1 < k) return;
        // 2. 递归终止:每个分支走完了集合宽度
        if(startIndex == n + 1) {
            ans.push_back(opt);
            return;
        }
        // 3. 本层逻辑:递归判断每个元素选或不选,递归与回溯一一对应
        // 不选-继续递归判断下个位置
        dfs(n, k, startIndex + 1);
        // 选-将当前选择的元素放入opt数组记录,再继续下轮循环
        opt.push_back(startIndex);   // 添加
        dfs(n, k, startIndex + 1);
        opt.pop_back();              // 回溯
    }
};

回溯思路:遍历整棵树-找到所有组合的叶子节点

把组合问题抽象为如下树形结构:

77.组合

发现n相当于树的宽度,k相当于树的深度

图中每次搜索到了叶子节点,我们就找到了一个结果

  • 相当于只需要把达到叶子节点的结果收集起来,就可以求得 n个数中k个数的组合集合

函数里一定有两个参数,既然是集合n里面取k个数,那么n和k是两个int型的参数。

然后还需要一个参数,为int型变量startIndex,这个参数用来记录本层递归的中,集合从哪里开始遍历(集合就是[1,…,n] )。

  • 即每次按住一个数,然后往后找可能的组合
class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(int n, int k, int startIndex) {
        if (path.size() == k) {
            result.push_back(path);
            return;
        }
        // 每层按住一个数-集合宽度
        // 一直递归到边界-叶子节点
        // 剪枝优化:继续走下去:当前小于k,后面的数都选上>=k
        //          path.size() + n - i + 1 >= k
        for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) { 
            path.push_back(i);         // 处理节点-先按住一端
            backtracking(n, k, i + 1); // 递归寻找与i的组合
            path.pop_back();           // 回溯,撤销处理的节点,找另个与i的组合
        }
    }
public:
    vector<vector<int>> combine(int n, int k) {
        backtracking(n, k, 1);
        return result;
    }
};

17.电话号码的字母组合

题解

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

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

img

示例 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'] 的一个数字。

搜索解题思路

在这里插入图片描述

字母组合-子集问题

  • 1.提取信息

    • 给定号码,每个号码有三个字母可以选择,
    • n个字母,共有3^n中可能,是子集问题,指数问题
  • 2.定义状态:

    • 号码是给定的,
    • 动态改变的状态是:当前处理第几个号码index,以及每个号码选择的字母组合后的子串str
  • 3.定义搜索框架:

    • 子集问题,需要深度遍历,记录每个分支的结果,因此是DFS

    • 状态先作为参数,

    • 递归边界:index遍历到最后一个号码

    • 如果局部变量,不用还原现场,代码优化时,状态作为全局变量,要还原现场

    • 每个号码的字母不重复,因此无需判重

class Solution {
public:
    vector<string> letterCombinations(string digits) {
        if(digits.empty()) return ans;
        this->digits = digits;
        // 初始化数据
        map.insert(pair<char, string>('2', "abc"));
        map.insert(pair<char, string>('3', "def"));
        map.insert(pair<char, string>('4', "ghi"));
        map.insert(pair<char, string>('5', "jkl"));
        map.insert(pair<char, string>('6', "mno"));
        map.insert(pair<char, string>('7', "pqrs"));
        map.insert(pair<char, string>('8', "tuv"));
        map.insert(pair<char, string>('9', "wxyz"));
        dfs(0);
        return ans;
    }
private:
    vector<string> ans;
    string opt = "";     // 全局临时变量
    string digits;
    unordered_map<char, string> map;
    void dfs(int index) {
        // 1. 终止条件:数字遍历完了,记录一个答案
        if(index == digits.size()) {
            ans.push_back(opt); return;
        }
        // 2. 本层逻辑:取数字中的一种字母参与组合
        for(char c : map[digits[index]]) {
            opt.push_back(c);
            dfs(index + 1);
            opt.pop_back();
        }
    }
};

39. 组合总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

提示:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • candidates 的所有元素 互不相同
  • 1 <= target <= 40

题解

没有限制个数。没有深度上的剪枝

// sum 在参数表中,自动回溯,包含相同i,i递归不变
 dfs(i, sum - candidates[i]); 
class Solution {
public:
   vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        // 1.先对数组进行排序,免得子集重复
        sort(candidates.begin(), candidates.end());
        this->candidates = candidates;
        dfs(0, target);
        return ans;
    }
private:
    vector<vector<int>> ans;
    vector<int> opt;
    vector<int> candidates;
    void dfs(int start, int sum) {
        // 1. 递归边界-找到一组结果
        if(sum == 0) {
            ans.push_back(opt); return;
        } 
        if(sum > 0) { // sum < 0剪枝掉
            // 没有限制个数。没有深度上的剪枝
            for(int i = start; i < candidates.size(); i++) { 
                opt.push_back(candidates[i]);
                // sum 在参数表中,自动回溯,包含相同i,i递归不变
                dfs(i, sum - candidates[i]); 
                opt.pop_back();
            }
        }
    }
};

40. 组合总和 II

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

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

**注意:**解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]

提示:

  • 1 <= candidates.length <= 100
  • 1 <= candidates[i] <= 50
  • 1 <= target <= 30

题解

  • 没有限制个数。没有深度上的剪枝
  • 原数组元素不能相同,每次递归找下个位置
  • 同样,要先排序,去重

注意:

都知道组合问题可以抽象为树形结构,那么“使用过”在这个树形结构上是有两个维度的,一个维度是同一树枝上使用过,一个维度是同一树层上使用过。没有理解这两个层面上的“使用过” 是造成大家没有彻底理解去重的根本原因。

那么问题来了,我们是要同一树层上使用过,还是同一树枝上使用过呢?

回看一下题目,元素在同一个组合内是可以重复的,怎么重复都没事,但两个组合不能相同。

所以我们要去重的是同一树层上的“使用过”,同一树枝上的都是一个组合里的元素,不用去重

强调一下,树层去重的话,需要对数组排序!

选择过程树形结构如图所示:

40.组合总和II

class Solution {
public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {      
        sort(candidates.begin(), candidates.end()); // 先排序,再改变指向
        this->candidates = candidates;
        dfs(0, target);
        return ans;
    }
private:
    vector<vector<int>> ans;
    vector<int> opt;
    vector<int> candidates;
    void dfs(int start, int sum) {
        if(sum == 0) {
            ans.push_back(opt);
            return;
        }
        if(sum > 0) {
            for(int i = start; i < candidates.size(); i++) {
                // 注意:对同一树层上相同的两个元素要去重
                // 例如[1,1,2],已经选取了0,1号的[1,2],不能再选1,2号的[1,2]
                if(i > start && candidates[i] == candidates[i - 1])
                    continue;
                opt.push_back(candidates[i]);
                dfs(i + 1, sum - candidates[i]);
                opt.pop_back();
            }
        }
    }
};

216. 组合总和 III

找出所有相加之和为 nk 个数的组合,且满足下列条件:

  • 只使用数字1到9
  • 每个数字 最多使用一次

返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

示例 1:

输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。

示例 2:

输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
解释:
1 + 2 + 6 = 9
1 + 3 + 5 = 9
2 + 3 + 4 = 9
没有其他符合的组合了。

示例 3:

输入: k = 4, n = 1
输出: []
解释: 不存在有效的组合。
在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。

提示:

  • 2 <= k <= 9
  • 1 <= n <= 60

题解

递归枚举

  • 与递归枚举思路相同,只是在组合后的结果中,进行判断
class Solution {
public:
    vector<vector<int>> combinationSum3(int k, int n) {
        dfs(k, n, 1);
        return ans;
    }
private:
    vector<vector<int>> ans;
    vector<int> opt;
    void dfs(int k, int n, int i) {
        // 1. 剪枝
        if(opt.size() > k || opt.size() + 9 - i + 1 < k) return;
        // 2. 递归终止:遇到叶子节点-处理一个分支的结果
        if(i > 9) {
            int sum = 0;
            for(int j : opt) sum += j;
            if(sum == n) ans.push_back(opt);
        }
        // 3. 本层逻辑
        // 选
        opt.push_back(i);
        dfs(k, n, i + 1);
        opt.pop_back();
        // 不选
        dfs(k, n, i + 1);
    }
};

递归回溯

class Solution {
public:
    vector<vector<int>> combinationSum3(int k, int n) {
        dfs(k, n, 1);
        return ans;
    }
private:
    vector<vector<int>> ans;
    vector<int> opt;
    void dfs(int k, int sum, int start) { // sum 为target-i
       // 1.先剪枝:提出总和不满足的情况
       if(sum < 0 || opt.size() > k) return;
       // 1.递归终止条件:先触发k个元素
       if(sum == 0 && opt.size() == k) {
           ans.push_back(opt); return;
       }
       // 剩下情况就是sum > 0
       for(int i = start; opt.size() + 9 - i + 1 >= k; i++) {
           opt.push_back(i);
           dfs(k, sum - i, i + 1);
           opt.pop_back();
       }
    }
};

二、切割问题

三、子集问题

四、排列问题

五、棋盘问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/191321.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GAMES101笔记:辐射度量学(上)

Radiometry 辐射度量学 如何描述光照&#xff0c;定义了一系列的方法和单位准确度量光的空间属性&#xff1a; Radiant fluxintensityirradianceradiance 以物理正确的方式进行光照计算 Radiant Energy and Flux (Power) Radiant Energy 定义 Radiant Energy 是电磁辐射的能…

6、数据的合并

目录 一、添加变量即横向合并。 二、添加个案即纵向合并 在实际工作中&#xff0c;为了提高效率&#xff0c;经常需要将一份数据分成几部分分别录入&#xff0c;或为了便于分析&#xff0c;又将几个数据文件合并成一个总的数据文件。为此&#xff0c;SPSS提供了两种合并数据文…

【7】【vue】【vue3】

1、项目结构&#xff1a; asserts用来存放静态资源的&#xff08;一些图片&#xff0c;公共的css文件等&#xff09; components用来存放vue的组件&#xff08;vue是组件开发&#xff09; App.vue是主入口组件 &#xff08;根组件&#xff0c;所有组件都是从这里开始&#xff…

在 PyTorch 中使用梯度检查点在GPU 上训练更大的模型

作为机器学习从业者&#xff0c;我们经常会遇到这样的情况&#xff0c;想要训练一个比较大的模型&#xff0c;而 GPU 却因为内存不足而无法训练它。当我们在出于安全原因不允许在云计算的环境中工作时&#xff0c;这个问题经常会出现。在这样的环境中&#xff0c;我们无法足够快…

万向节锁问题

以前一直听说过万向节锁当时觉得问题太难就没去认真分析最近在B站找了一些视频看懂了。简单来说旋转是有顺序的&#xff0c;比如transform面板有三个旋转分量&#xff0c;你先调整y&#xff0c;再调整x&#xff0c;最后调整z按照正常思路来说&#xff0c;调整x轴是在y轴旋转的基…

想成为数据分析师,看这里,数据分析必备的43个Excel函数

目录 前言 函数分类&#xff1a; 关联匹配类清洗处理类逻辑运算类计算统计类时间序列类 前言 Excel是我们工作中经常使用的一种工具&#xff0c;对于数据分析来说&#xff0c;这也是处理数据最基础的工具。 很多传统行业的数据分析师甚至只要掌握Excel和SQL即可。 对于初学者…

【DataX】datax | datax-web | win搭建datax-web环境

一、环境准备 1、jdk8 2、maven 3、mysql7 4、python3 5、window10 6、idea 7、2345解压&#xff08;win支持tar.gz解压&#xff09; 8、git 二、操作步骤 1、datax操作步骤 1&#xff09;下载datax http://datax-opensource.oss-cn-hangzhou.aliyuncs.com/datax.tar.gz 2&am…

ES6 环境下 Openlayers 集成使用 ol-ext 以及在线示例

ES6 环境下 Openlayers 集成使用 ol-ext 以及在线示例ol-ext 简介版本说明打包后体积集成方式在线示例最近打算重新封装一下 Openlayers&#xff0c;方便前端人员使用&#xff0c;基础功能没什么可说的&#xff0c;毕竟 Openlayers 的示例和 API 已经非常友好了。 想增加一些地…

2023-01-31 CSDN问答中如何防止和惩罚 “偷代码操作“

CSDN问答中如何防止和惩罚 "偷代码操作"前言一. 代码隐藏保护(CSDN官方回复目前无此功能)二. 先占位后抄袭的处理三. 编辑记录是照妖镜总结前言 随着问答的参与时间累积, 逐渐的碰到了一些问题, 常在河边走, 怎能不湿鞋, 原先看到抄代码结果原创没被采纳, 只能报以同…

AcWing 10. 有依赖的背包问题(分组背包问题 + 树形DP)

AcWing 10. 有依赖的背包问题&#xff08;分组背包问题 树形DP&#xff09;一、问题二、分析1、整体分析2、状态表示3、状态转移4、循环设计5、初末状态三、代码一、问题 二、分析 1、整体分析 这道题其实就是作者之前讲解过的一道题&#xff1a;AcWing 487. 金明的预算方案…

【双向链表】数据结构双向链表的实现

前言&#xff1a; 前一期我们已经学习过单链表了&#xff0c;今天我们来学习链表中的双向链表&#xff01; 目录1.概念以及结构2.双向链表结点结构体3.接口实现3.1动态申请一个结点3.2初始化链表3.3打印链表3.4双向链表尾插3.5 双向链表尾删3.6双向链表头插3.7双向链表头删3.8双…

Linux常用命令——pvscan命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) pvscan 扫描系统中所有硬盘的物理卷列表 补充说明 pvscan命令会扫描系统中连接的所有硬盘&#xff0c;列出找到的物理卷列表。使用pvscan命令的-n选项可以显示硬盘中的不属于任何卷组的物理卷&#xff0c;这些…

OAuth2代码演示

目录 1 创建项目结构 1.1 客户 1.2 认证服务器 1.3 资源拥有者 1.4 资源服务器 client 客户 authorization-server 认证服务 resource-owner 资源所有者 resource-server 资源服务器 工作流程&#xff1a; 客户向资源所有者申请授权码 资源所有者下发授权码 客户拿到授权…

springboot+mongodb初体验

MongoDB 是一个基于分布式文件存储的数据库。由 C 语言编写&#xff0c;旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。 MongoDB 是一个介于关系数据库和非关系数据库之间的产品&#xff0c;是非关系数据库当中功能最丰富&#xff0c;最像关系数据库的。 1、mongodb服务…

JavaScript 算术运算符

JavaScript 算术运算符 加减乘除以及取模&#xff08;求余数&#xff09;、、– <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" />…

代码随想录算法训练营第十六天 | 104.二叉树的最大深度、559.n叉树的最大深度,111.二叉树的最小深度,222.完全二叉树的节点个数

Day15 周日休息一、参考资料二叉树的最大深度 &#xff08;优先掌握递归&#xff09;题目链接/文章讲解/视频讲解&#xff1a; https://programmercarl.com/0104.%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%9C%80%E5%A4%A7%E6%B7%B1%E5%BA%A6.html 二叉树的最小深度 &#xff08…

车载网络 - BootLoader - CAN/CANFD刷写前提

刷写作为车载网络测试极其重要的一个模块一直拖到今天才开始写,之前确实没有一个太好的想法怎么介绍这一块,虽然现在也没有想出来怎么写能够更好的介绍这块的内容,不过我也尽量用通俗的语言让大家看懂。 刷写流程 刷写流程我也根据用例的设计分为3个阶段:前置条件、刷写程序…

UDP+有穷自动状态机构造网络指令系统

UDP有穷自动状态机构造网络指令系统 项目背景 某展厅的小项目&#xff0c;使用Unity制作了一个视频播放器&#xff0c;作为受控端&#xff0c;需要接收解说员手中的“PAD”或“触控屏电脑”等设备发来的控制指令。要求指令系统满足以下功能&#xff1a; 能够随意切换要播放的…

剑指Offer 第17天 Top K问题 优先级队列解决数据流中位数

目录 剑指 Offer 40. 最小的k个数 剑指 Offer 41. 数据流中的中位数 剑指 Offer 40. 最小的k个数 输入整数数组 arr &#xff0c;找出其中最小的 k 个数。例如&#xff0c;输入4、5、1、6、2、7、3、8这8个数字&#xff0c;则最小的4个数字是1、2、3、4。 示例 1&#xff1a; …

图像处理中的微分算子

摘要 微分算子在图像处理中的作用主要是用在图像的边缘检测&#xff0c;而图像边缘检测必须满足两个条件&#xff1a;一能有效的抑制噪声&#xff0c;二能必须尽量精确定位边缘位置。现在常用的微分算子主要有&#xff1a;Sobel算子&#xff0c;Robert算子&#xff0c;Prewitt…