二叉树:以为使用了递归,其实还隐藏着回溯
还在玩耍的你,该总结啦!(本周小结之二叉树)中求100.相同的树的代码中,我笔误贴出了 求对称树的代码了,细心的同学应该都发现了。
那么如下我再给出求100. 相同的树 的代码,如下:
class Solution {
public:
bool compare(TreeNode* tree1, TreeNode* tree2) {
if (tree1 == NULL && tree2 != NULL) return false;
else if (tree1 != NULL && tree2 == NULL) return false;
else if (tree1 == NULL && tree2 == NULL) return true;
else if (tree1->val != tree2->val) return false; // 注意这里我没有使用else
// 此时就是:左右节点都不为空,且数值相同的情况
// 此时才做递归,做下一层的判断
bool compareLeft = compare(tree1->left, tree2->left); // 左子树:左、 右子树:左
bool compareRight = compare(tree1->right, tree2->right); // 左子树:右、 右子树:右
bool isSame = compareLeft && compareRight; // 左子树:中、 右子树:中(逻辑处理)
return isSame;
}
bool isSameTree(TreeNode* p, TreeNode* q) {
return compare(p, q);
}
};
以上的代码相对于:二叉树:我对称么? 仅仅修改了变量的名字(为了符合判断相同树的语境)和 遍历的顺序。
大家应该会体会到:「认清判断对称树本质之后, 对称树的代码 稍作修改 就可以直接用来AC 100.相同的树。」
递归中隐藏着回溯
在二叉树:找我的所有路径?中我强调了本题其实是用到了回溯的,并且给出了第一个版本的代码,把回溯的过程充分的体现了出来。
如下的代码充分的体现出回溯:(257. 二叉树的所有路径)
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;
}
};
如下为精简之后的递归代码:(257. 二叉树的所有路径) 注意注释
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;
}
};
上面精简之后的代码,大家貌似感受不到回溯了,其实「回溯就隐藏在traversal(cur->left, path + "->", result);中的 path + "->"。 每次函数调用完,path依然是没有加上"->" 的,这就是回溯了。」
为了把这份精简代码的回溯过程展现出来,大家可以试一试吧:
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依然是之前的数值(相当于回溯了)」
如果有点遗忘了,建议把这篇二叉树:找我的所有路径?在仔细看一下,然后再看这里的总结,相信会豁然开朗。
这里我尽量把逻辑的每一个细节都抠出来展现了,希望对大家有所帮助!
最近Carl也在出游,都是在路上挤时间写文章,如果哪里有出入,欢迎大家多多纠正哈。
在留言区留下你的思路吧!
- WordPress在RSS Feed 中输出自定义特色图像(缩略图)
- 单拼域名can.com以高达99.2万元成交!
- CI学习 CCNET Config 第一天
- IBatisNet基础组件
- 学习altas笔记[客户端JS和Altas环境初始化关系和DataTable返回数据的客户端处理]
- 在Windows 8 Hyper-V下的安装CentOS 6和SSH配置
- WordPress RSS Feed 优化/设置技巧六则
- SourceTree 基本介绍
- 学习Altas 笔记[JS简单调用服务端方法]
- 使用WinSCP软件在windows和Linux中进行文件传输
- 线程安全的Generic Dictionary
- Python 项目实践三(Web应用程序)第五篇
- CentOS 6.3下 安装 Mono 3.2 和Jexus 5.4
- Python 项目实践三(Web应用程序)第四篇
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Windows10下使用VS2017编译和使用yaml-cpp库
- CentOS7下编译yaml-cpp库
- SAP Spartacus CurrentProductService返回的null对象
- SAP Spartacus产品明细页面用Observable显示产品名称
- 关于rxjs里operators filter和map的详细讨论
- 用代码查看SAP Spartacus购物车内的行项目
- rxjs的map和switchMap在SAP Spartacus中的应用
- 用代码查看SAP Spartacus购物车内的行项目
- rxjs fromEvent的用法
- Python2和Python3的区别简单总结
- Django操作数据库
- Hive元数据服务MetaStore
- Linux---Shell脚本字符显示特殊颜色效果
- 快速学习-RocketMQ运维管理
- Django常用语句