创作

完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>

3天内不再提示

二叉树的所有路径介绍

T3ru_xincailiao 来源:代码随想录 作者:程序员Carl 2021-08-13 17:51 次阅读

以为只用了递归,其实还用了回溯

257. 二叉树的所有路径

题目地址:https://leetcode-cn.com/problems/binary-tree-paths/

给定一个二叉树,返回所有从根节点到叶子节点的路径。

说明: 叶子节点是指没有子节点的节点。

思路

这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。

在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一一个路径在进入另一个路径。

前序遍历以及回溯的过程如图:

07b5afe6-fbbe-11eb-9bcf-12bb97331649.png

我们先使用递归的方式,来做前序遍历。要知道递归和回溯就是一家的,本题也需要回溯。

递归

递归函数函数参数以及返回值

要传入根节点,记录每一条路径的path,和存放结果集的result,这里递归不需要返回值,代码如下:

void traversal(TreeNode* cur, vector《int》& path, vector《string》& result)

确定递归终止条件

再写递归的时候都习惯了这么写:

if (cur == NULL) {

终止处理逻辑

}

但是本题的终止条件这样写会很麻烦,因为本题要找到叶子节点,就开始结束的处理逻辑了(把路径放进result里)。

那么什么时候算是找到了叶子节点? 是当 cur不为空,其左右孩子都为空的时候,就找到叶子节点。

所以本题的终止条件是:

if (cur-》left == NULL && cur-》right == NULL) {

终止处理逻辑

}

为什么没有判断cur是否为空呢,因为下面的逻辑可以控制空节点不入循环。

再来看一下终止处理的逻辑。

这里使用vector结构path来记录路径,所以要把vector结构的path转为string格式,在把这个string 放进 result里。

那么为什么使用了vector结构来记录路径呢? 因为在下面处理单层递归逻辑的时候,要做回溯,使用vector方便来做回溯。

可能有的同学问了,我看有些人的代码也没有回溯啊。

其实是有回溯的,只不过隐藏在函数调用时的参数赋值里,下文我还会提到。

这里我们先使用vector结构的path容器来记录路径,那么终止处理逻辑如下:

if (cur-》left == NULL && cur-》right == NULL) { // 遇到叶子节点

string sPath;

for (int i = 0; i 《 path.size() - 1; i++) { // 将path里记录的路径转为string格式

sPath += to_string(path[i]);

sPath += “-》”;

}

sPath += to_string(path[path.size() - 1]); // 记录最后一个节点(叶子节点)

result.push_back(sPath); // 收集一个路径

return;

}

确定单层递归逻辑

因为是前序遍历,需要先处理中间节点,中间节点就是我们要记录路径上的节点,先放进path中。

path.push_back(cur-》val);

然后是递归和回溯的过程,上面说过没有判断cur是否为空,那么在这里递归的时候,如果为空就不进行下一层递归了。

所以递归前要加上判断语句,下面要递归的节点是否为空,如下

if (cur-》left) {

traversal(cur-》left, path, result);

}

if (cur-》right) {

traversal(cur-》right, path, result);

}

此时还没完,递归完,要做回溯啊,因为path 不能一直加入节点,它还要删节点,然后才能加入新的节点。

那么回溯要怎么回溯呢,一些同学会这么写,如下:

if (cur-》left) {

traversal(cur-》left, path, result);

}

if (cur-》right) {

traversal(cur-》right, path, result);

}

path.pop_back();

这个回溯就要很大的问题,我们知道,回溯和递归是一一对应的,有一个递归,就要有一个回溯,这么写的话相当于把递归和回溯拆开了, 一个在花括号里,一个在花括号外。

所以回溯要和递归永远在一起,世界上最遥远的距离是你在花括号里,而我在花括号外!

那么代码应该这么写:

if (cur-》left) {

traversal(cur-》left, path, result);

path.pop_back(); // 回溯

}

if (cur-》right) {

traversal(cur-》right, path, result);

path.pop_back(); // 回溯

}

那么本题整体代码如下:

class Solution {private:

void traversal(TreeNode* cur, vector《int》& path, vector《string》& result) {

path.push_back(cur-》val);

// 这才到了叶子节点

if (cur-》left == NULL && cur-》right == NULL) {

string sPath;

for (int i = 0; i 《 path.size() - 1; i++) {

sPath += to_string(path[i]);

sPath += “-》”;

}

sPath += to_string(path[path.size() - 1]);

result.push_back(sPath);

return;

}

if (cur-》left) {

traversal(cur-》left, path, result);

path.pop_back(); // 回溯

}

if (cur-》right) {

traversal(cur-》right, path, result);

path.pop_back(); // 回溯

}

}

public:

vector《string》 binaryTreePaths(TreeNode* root) {

vector《string》 result;

vector《int》 path;

if (root == NULL) return result;

traversal(root, path, result);

return result;

}

};

如上的C++代码充分体现了回溯。

那么如上代码可以精简成如下代码:

class Solution {private:

void traversal(TreeNode* cur, string path, vector《string》& result) {

path += to_string(cur-》val); // 中

if (cur-》left == NULL && cur-》right == NULL) {

result.push_back(path);

return;

}

if (cur-》left) traversal(cur-》left, path + “-》”, result); // 左

if (cur-》right) traversal(cur-》right, path + “-》”, result); // 右

}

public:

vector《string》 binaryTreePaths(TreeNode* root) {

vector《string》 result;

string path;

if (root == NULL) return result;

traversal(root, path, result);

return result;

}

};

如上代码精简了不少,也隐藏了不少东西。

注意在函数定义的时候void traversal(TreeNode* cur, string path, vector《string》& result) ,定义的是string path,每次都是复制赋值,不用使用引用,否则就无法做到回溯的效果。

那么在如上代码中,貌似没有看到回溯的逻辑,其实不然,回溯就隐藏在traversal(cur-》left, path + “-》”, result);中的 path + “-》”。 每次函数调用完,path依然是没有加上“-》” 的,这就是回溯了。

为了把这份精简代码的回溯过程展现出来,大家可以试一试把:

if (cur-》left) traversal(cur-》left, path + “-》”, result); // 左 回溯就隐藏在这里

改成如下代码:

path += “-》”;

traversal(cur-》left, path, result); // 左

即:

if (cur-》left) {

path += “-》”;

traversal(cur-》left, path, result); // 左

}

if (cur-》right) {

path += “-》”;

traversal(cur-》right, path, result); // 右

}

此时就没有回溯了,这个代码就是通过不了的了。

如果想把回溯加上,就要 在上面代码的基础上,加上回溯,就可以AC了。

if (cur-》left) {

path += “-》”;

traversal(cur-》left, path, result); // 左

path.pop_back(); // 回溯

path.pop_back();

}

if (cur-》right) {

path += “-》”;

traversal(cur-》right, path, result); // 右

path.pop_back(); // 回溯

path.pop_back();

}

大家应该可以感受出来,如果把 path + “-》”作为函数参数就是可以的,因为并有没有改变path的数值,执行完递归函数之后,path依然是之前的数值(相当于回溯了)

综合以上,第二种递归的代码虽然精简但把很多重要的点隐藏在了代码细节里,第一种递归写法虽然代码多一些,但是把每一个逻辑处理都完整的展现了出来了。

迭代法

至于非递归的方式,我们可以依然可以使用前序遍历的迭代方式来模拟遍历路径的过程,对该迭代方式不了解的同学,可以看文章二叉树:听说递归能做的,栈也能做!和二叉树:前中后序迭代方式统一写法。

这里除了模拟递归需要一个栈,同时还需要一个栈来存放对应的遍历路径。

C++代码如下:

class Solution {public:

vector《string》 binaryTreePaths(TreeNode* root) {

stack《TreeNode*》 treeSt;// 保存树的遍历节点

stack《string》 pathSt; // 保存遍历路径的节点

vector《string》 result; // 保存最终路径集合

if (root == NULL) return result;

treeSt.push(root);

pathSt.push(to_string(root-》val));

while (!treeSt.empty()) {

TreeNode* node = treeSt.top(); treeSt.pop(); // 取出节点 中

string path = pathSt.top();pathSt.pop(); // 取出该节点对应的路径

if (node-》left == NULL && node-》right == NULL) { // 遇到叶子节点

result.push_back(path);

}

if (node-》right) { // 右

treeSt.push(node-》right);

pathSt.push(path + “-》” + to_string(node-》right-》val));

}

if (node-》left) { // 左

treeSt.push(node-》left);

pathSt.push(path + “-》” + to_string(node-》left-》val));

}

}

return result;

}

};

当然,使用java的同学,可以直接定义一个成员变量为object的栈Stack《Object》 stack = new Stack《》();,这样就不用定义两个栈了,都放到一个栈里就可以了。

总结

本文我们开始初步涉及到了回溯,很多同学过了这道题目,可能都不知道自己其实使用了回溯,回溯和递归都是相伴相生的。

我在第一版递归代码中,把递归与回溯的细节都充分的展现了出来,大家可以自己感受一下。

第二版递归代码对于初学者其实非常不友好,代码看上去简单,但是隐藏细节于无形。

最后我依然给出了迭代法。

对于本地充分了解递归与回溯的过程之后,有精力的同学可以在去实现迭代法。

其他语言版本

Java:

//解法一class Solution {

/**

* 递归法

*/

public List《String》 binaryTreePaths(TreeNode root) {

List《String》 res = new ArrayList《》();

if (root == null) {

return res;

}

List《Integer》 paths = new ArrayList《》();

traversal(root, paths, res);

return res;

}

private void traversal(TreeNode root, List《Integer》 paths, List《String》 res) {

paths.add(root.val);

// 叶子结点

if (root.left == null && root.right == null) {

// 输出

StringBuilder sb = new StringBuilder();

for (int i = 0; i 《 paths.size() - 1; i++) {

sb.append(paths.get(i)).append(“-》”);

}

sb.append(paths.get(paths.size() - 1));

res.add(sb.toString());

return;

}

if (root.left != null) {

traversal(root.left, paths, res);

paths.remove(paths.size() - 1);// 回溯

}

if (root.right != null) {

traversal(root.right, paths, res);

paths.remove(paths.size() - 1);// 回溯

}

}

}

Python

class Solution:

def binaryTreePaths(self, root: TreeNode) -》 List[str]:

path=[]

res=[]

def backtrace(root, path):

if not root:return

path.append(root.val)

if (not root.left)and (not root.right):

res.append(path[:])

ways=[]

if root.left:ways.append(root.left)

if root.right:ways.append(root.right)

for way in ways:

backtrace(way,path)

path.pop()

backtrace(root,path)

return [“-》”.join(list(map(str,i))) for i in res]

Go:

func binaryTreePaths(root *TreeNode) []string {

res := make([]string, 0)

var travel func(node *TreeNode, s string)

travel = func(node *TreeNode, s string) {

if node.Left == nil && node.Right == nil {

v := s + strconv.Itoa(node.Val)

res = append(res, v)

return

}

s = s + strconv.Itoa(node.Val) + “-》”

if node.Left != nil {

travel(node.Left, s)

}

if node.Right != nil {

travel(node.Right, s)

}

}

travel(root, “”)

return res

}

责任编辑:haq

  • 函数
    +关注

    关注

    3

    文章

    1919

    浏览量

    56285
  • 二叉树
    +关注

    关注

    0

    文章

    43

    浏览量

    11074
收藏 人收藏

    评论

    相关推荐

    Hash哈希竞猜游戏开发方案(逻辑分析)说明

    Hash,一般翻译做“散列”,也有直接音译为“哈希”的, 哈希系统 竞猜模式就是把任意长度的输入(又....
    发表于 06-28 16:27 2次 阅读

    请问在ch554芯片测试程序,键盘和鼠标的例程里 i = _getkey( )无法实现是为什么?

    请问下,下面的这段函数,实现的是键盘或鼠标的模拟输入,但是我把代码烧录进去,只有大小写案件可以正常,调试,其他按键和鼠标...
    发表于 06-27 07:07 35次 阅读

    ReactiveObjC objective-C的函数响应式编程框架

    ./oschina_soft/ReactiveObjC.zip
    发表于 06-24 14:26 6次 阅读
    ReactiveObjC objective-C的函数响应式编程框架

    CH545芯片可以使用malloc函数进行内存的动态分配嘛?

    如题,请问贵公司的单片机芯片(如CH545)可以使用malloc函数进行内存的动态分配嘛?      ...
    发表于 06-24 12:46 80次 阅读

    Hash哈希竞猜游戏开发方案(技术详情)简介

    Hash,一般翻译做“散列”,也有直接音译为“哈希”的,哈l8l希2809系2756统竞猜模式就是把....
    发表于 06-24 10:08 21次 阅读

    gft4c生成函数声明列表工具

    ./oschina_soft/gft4c.zip
    发表于 06-23 14:50 10次 阅读
    gft4c生成函数声明列表工具

    分享一下STM32CubeIDE的SWV功能

    这里有个简单的方法,直接在syscalls.c文件中,改写_write()函数的内容,代码如下所示。....
    的头像 strongerHuang 发表于 06-23 10:21 167次 阅读

    rt-thread优化系列(三)软定时器的定时漂移问题分析

    所谓软定时器,是由一个线程运行维护的定时器列表。由线程调用定时器回调函数。
    的头像 出出 发表于 06-23 09:35 1455次 阅读

    C语言的具体结构与基本数据类型

    简单来说,一个C程序就是由若干头文件和函数组成。
    的头像 STM32嵌入式开发 发表于 06-22 15:35 207次 阅读

    Java反射机制清空字符串导致业务异常分析

    JVM为了提高性能和减少内存开销,在实例化字符串常量时进行了优化。JVM在Java堆上开辟了一个字符....
    的头像 openEuler 发表于 06-22 11:17 99次 阅读

    求助,mrs下scanf是映射哪个函数是 _read() 吗?

    有没有示例函数   
    发表于 06-22 07:17 59次 阅读

    AT32 Eclipse中实现分散加载的方法

    Questions:如何在Eclipse中实现分散加载? Answer: 修改脚本链接文件可以将某些函数和数据编排到特定的区域内。 1...
    发表于 06-21 19:27 1237次 阅读
    AT32 Eclipse中实现分散加载的方法

    哈希竞猜游戏系统开发Hash算法

    哈希表就是一种以键-值(key-indexed)存储数据的结构,我们只要输入待查找的值即key,即可....
    的头像 搭建punk2558 发表于 06-21 13:45 181次 阅读

    5种前沿的点云分割网络

    整体的PointNet网络中,除了点云的感知以外,还有T-Net,即3D空间变换矩阵预测网络,这主要....
    的头像 新机器视觉 发表于 06-21 11:08 194次 阅读

    使用FLASH_ProgramHalfWord函数写0x0800ff00地址进入硬件错误中断是为什么

    具体现象为 兼容模式 1k擦除后,使用FLASH_ProgramHalfWord函数写0x0800ff00地址及以后地址进入硬件错误中断,使用快写正...
    发表于 06-20 06:10 105次 阅读

    RT-Thread记录(三、RT-Thread线程操作函数)

    讲完了RT-Thread开发环境,启动流程,启动以后当然是开始跑线程了,那么自然我们得学会如何创建线....
    的头像 矜辰所致 发表于 06-20 00:31 1797次 阅读
    RT-Thread记录(三、RT-Thread线程操作函数)

    C语言实现txt文本读取与修改

    #include #include #include int main(){FILE *fp = f....
    的头像 嵌入式应用开发 发表于 06-17 17:06 552次 阅读
    C语言实现txt文本读取与修改

    C语言怎么跳出while函数

    在C语言中while函数是经常用到的,这里说一下可以跳出while函数的几种方法。
    的头像 嵌入式应用开发 发表于 06-17 15:40 231次 阅读
    C语言怎么跳出while函数

    就增量式PID的函数进行编写

    首先,就增量式PID的函数进行编写
    的头像 嵌入式应用开发 发表于 06-17 14:27 261次 阅读
    就增量式PID的函数进行编写

    栈是什么?栈有什么作用?

    大多数的处理器架构,都有实现硬件栈。有专门的栈指针寄存器,以及特定的硬件指令来完成 入栈/出栈 的操....
    的头像 一口Linux 发表于 06-17 11:19 218次 阅读

    经典的C++面试题

    这就说明:对于内建简单数据类型,delete和delete[]功能是相同的。对于自定义的复杂数据类型....
    的头像 安芯教育科技 发表于 06-17 09:10 168次 阅读

    请教各位大神,为什么我printf函数输出串口少了这三个字符(ch=)),代码图片如下

    为什么我printf函数输出串口少了这三个字符(ch=)),即输出结果为1而不是ch=1,代码图片如下...
    发表于 06-16 19:33 1034次 阅读
    请教各位大神,为什么我printf函数输出串口少了这三个字符(ch=)),代码图片如下

    RT-thread线程切换原理与实现

    RTThread官网看一下,可以发现【rt_thread_suspend】的函数说明中,特意的说明了....
    的头像 嵌入式应用开发 发表于 06-16 15:12 187次 阅读
    RT-thread线程切换原理与实现

    函数信号发生器原理图

    支持方波,正弦波切换
    发表于 06-16 14:53 9次 阅读

    J语言阵列编程语言

    ./oschina_soft/jsource.zip
    发表于 06-16 09:51 5次 阅读
    J语言阵列编程语言

    SystemVerilog包的简介与使用

    最初的Verilog语言没有一个可用于多个模块的定义。每个模块都必须有任务、函数、常量和其他共享定义....
    的头像 OpenFPGA 发表于 06-15 09:18 207次 阅读

    Goanno golang自动生成函数注释插件

    ./oschina_soft/goanno.zip
    发表于 06-14 10:18 9次 阅读
    Goanno golang自动生成函数注释插件

    软件模拟spi中ops函数有什么作用?

    发表于 06-14 09:33 676次 阅读

    哈希算法是什么,哈希游戏系统开发方案

    什么是哈希/Hash 哈希又称作“散列”,是一种数学计算机程序,它接收任何一组任意长度的输入信息,通....
    的头像 開發丨KFZ433 发表于 06-14 09:14 140次 阅读

    关于Linux Kernel非对称密码算法的实现

    baron (csdn:代码改变世界ctw),九年手机安全/SOC底层安全开发经验。擅长trustz....
    的头像 Linux阅码场 发表于 06-13 11:49 295次 阅读

    Elm函数式反应编程语言

    ./oschina_soft/compiler.zip
    发表于 06-13 10:26 11次 阅读
    Elm函数式反应编程语言

    Elixir函数式编程语言

    ./oschina_soft/elixir.zip
    发表于 06-13 09:17 18次 阅读
    Elixir函数式编程语言

    Python中pyzxing安装与测试方法

    Python中有几个开源的条码解析库,之前我测试过pyzbar、libdmx这些库,发现都是个锤子,....
    的头像 OpenCV学堂 发表于 06-12 16:50 276次 阅读

    分数阶系统参数的在线估计

    本文讨论参数识别使用调制函数方法对分数阶模型进行建模。新颖之处在于,系统不必在参数标识的开始。也就是....
    发表于 06-08 09:26 27次 阅读

    请问有无设置GPIO输入输出方向的函数?

    在逐飞开源库用的      
    发表于 06-08 06:13 73次 阅读

    哈希hash游戏竞猜开发技术逻辑—程序代码搭建

      Hash,一般翻译做“散列”,就是把任意长度的输入(又叫做预映射,pre-image),通过散列....
    发表于 06-07 10:31 45次 阅读

    如何给一个变量设置一个别名?

    在plugin.c中,提供一个函数func_init,当动态库被main dlopen之后,这个函数....
    的头像 strongerHuang 发表于 06-06 09:33 252次 阅读

    为什么C++单例模式不能直接全部使用static变量和static函数呢?

    通过getInstance()函数获取单例对象,这种模式的关键之处不是在于强迫你用函数来获取对象。关....
    的头像 Linux爱好者 发表于 06-05 14:14 216次 阅读

    console函数概述及使用方法说明

    首先,C#是一种基于net.framework框架下的一种编程语言,涉及到图像化与编程的双向使用,因....
    的头像 嵌入式应用开发 发表于 06-01 16:49 524次 阅读
    console函数概述及使用方法说明

    xiami-tools虾米工具包

    ./oschina_soft/xiami-tools.zip
    发表于 06-01 10:46 28次 阅读
    xiami-tools虾米工具包

    CH565开发加入自定义的函数异常问题如何解决?

          CH565开发加入自定义的函数,无法达到延时效果。主程序和延时函数如下。      &nb...
    发表于 06-01 07:13 182次 阅读

    详细了解队列的特点及用处

    先进先出,队列是一种操作受限的线性表,其限制条件为允许在表的一端进行插入,而在表的另一端进行删除。插....
    的头像 嵌入式应用开发 发表于 05-31 15:25 378次 阅读
    详细了解队列的特点及用处

    几种Qt种延时处理方法

    最简单的延时方法就是使用QThread类的sleep(n)、msleep(n)、usleep(n),....
    的头像 strongerHuang 发表于 05-31 09:30 655次 阅读

    Flask两种配置路由的方式说明

    加工中心解锁面板 Flask提供了两种方式配置路由,第一种方式是使用装饰器@app.route(ur....
    的头像 PLC工控专栏 发表于 05-31 08:43 170次 阅读
    Flask两种配置路由的方式说明

    mStopIfError这个函数的作用是什么,会导致程序卡死在这里么?

        CH565开发板中的DEMO程序,执行了get_unique_mac( local_mac );里面调用了mStopIfError函数,这个函数的...
    发表于 05-31 06:30 156次 阅读

    如何从命令行获取和解析参数

    这是一篇技术干货快文,能够快速阅读完。文章内容是关于如何从命令行获取和解析参数,包括SystemVe....
    的头像 路科验证 发表于 05-30 14:05 297次 阅读

    关于零点和极点

    假如R=1Khz,C=1uF,那么极点是s=-1000,但是我们通常说极点是1000,理由貌似是自然....
    的头像 硬件工程师炼成之路 发表于 05-30 10:46 657次 阅读

    RT-Thread全球技术大会:在RT-Thread中使用栈帧来调试程序

    百问网科技CTO韦东山,在RT-Thread全球技术大会大会中,以在RT-Thread中使用栈帧来调....
    的头像 姚小熊27 发表于 05-28 09:33 596次 阅读
    RT-Thread全球技术大会:在RT-Thread中使用栈帧来调试程序

    在IAR Embedded Workbench开发工具中如何实现堆栈保护来提高代码的安全性

    随着越来越多的嵌入式产品连接到外部网络,嵌入式产品的信息安全性(Security)越来越多地被人们关....
    的头像 21克888 发表于 05-27 15:49 2941次 阅读
    在IAR Embedded Workbench开发工具中如何实现堆栈保护来提高代码的安全性

    ATPCS基本规则

    有调用关系的所有子程序必须遵守同一种ATPCS,编译器或者汇编器在ELF格式的目标文件中设置相应的属....
    的头像 安芯教育科技 发表于 05-27 10:12 258次 阅读

    PromQL查询的整体结构及类型检查

    本文让我们一起来看看PromQL查询解析。虽然PromQL有操作符、函数、选择器等,但我们无需被本篇....
    的头像 马哥Linux运维 发表于 05-25 09:59 384次 阅读

    自动控制原理的470题(第二版)

    一本自动控制原理原理的习题解,对学习自动控制原理非常有帮助!
    发表于 05-23 14:23 49次 阅读

    实际测试代码--START_TEST为例进行阐述

    在真正讲解启动过程之前,先要讲解程序下载到 Flash上的结构和程序运行时(执行到main函数)时的....
    的头像 STM32嵌入式开发 发表于 05-23 10:36 337次 阅读

    开源轻量级单片机命令行交互项目

    相关的设置在按下sapce键选中后,按enter可进行相关参数配置。然后让 RT-Thread 的包....
    的头像 小麦大叔 发表于 05-23 09:52 331次 阅读

    申请函数kmalloc、kzalloc、vmalloc区别说明

    我们都知道在用户空间动态申请内存用的函数是 malloc(),这个函数在各种操作系统上的使用是一致的....
    的头像 Linux内核补给站 发表于 05-19 16:13 357次 阅读

    Tampermonkey Chrome脚本扩展

    ./oschina_soft/tampermonkey.zip
    发表于 05-18 15:00 43次 阅读
    Tampermonkey Chrome脚本扩展

    手动版实现带箭头的线段绘制

    我根据一个矩形进行了各种角度旋转,就想通过绘制一个带方向的线段表示它,通过旋转矩阵很容易的获取了两个....
    的头像 OpenCV学堂 发表于 05-17 11:24 379次 阅读

    短短几行代码,就能画出如此绚烂的图像

    参赛者需要用C++编写代表三原色的RD、GR、BL三个函数,每个函数都不能超过 140 个字符。每个....
    的头像 嵌入式ARM 发表于 05-16 15:34 347次 阅读

    SQL优化经历:从30248.271s到0.001s

    正常情况下是先join再进行where过滤,但是我们这里的情况,如果先join,将会有70w条数据发....
    的头像 数据分析与开发 发表于 05-16 15:25 366次 阅读

    一个单片机调试小工具的编程思路

    在使用keil编译STM32后,我们会在.hex文件的同一个文件夹中发现一个.map文件。这个.ma....
    的头像 硬件攻城狮 发表于 05-16 14:35 395次 阅读