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

公司变更名称嘉兴优化网站哪家好

公司变更名称,嘉兴优化网站哪家好,wordpress关注作者,网站建设目标的技术可行性给你一个二叉树的根节点 root #xff0c;按 任意顺序 #xff0c;返回所有从根节点到叶子节点的路径。 叶子节点 是指没有子节点的节点。 示例 1#xff1a; 输入#xff1a;root [1,2,3,null,5] 输出#xff1a;[1-2-5,1-3]示例 … 给你一个二叉树的根节点 root 按 任意顺序 返回所有从根节点到叶子节点的路径。 叶子节点 是指没有子节点的节点。 示例 1 输入root [1,2,3,null,5] 输出[1-2-5,1-3]示例 2 输入root [1] 输出[1]思路 这道题目要求从根节点到叶子的路径所以需要前序遍历这样才方便让父节点指向孩子节点找到对应的路径。 在这道题目中将第一次涉及到回溯因为我们要把路径记录下来需要回溯来回退一个路径再进入另一个路径。 前序遍历以及回溯的过程如图 我们先使用递归的方式来做前序遍历。要知道递归和回溯就是一家的本题也需要回溯。 #递归 递归函数参数以及返回值 要传入根节点记录每一条路径的path和存放结果集的result这里递归不需要返回值代码如下 void traversal(TreeNode* cur, vectorint path, vectorstring 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, vectorint path, vectorstring result) {path.push_back(cur-val); // 中中为什么写在这里因为最后一个节点也要加入到path中 // 这才到了叶子节点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:vectorstring binaryTreePaths(TreeNode* root) {vectorstring result;vectorint path;if (root NULL) return result;traversal(root, path, result);return result;} };如上的C代码充分体现了回溯。 那么如上代码可以精简成如下代码 class Solution { private:void traversal(TreeNode* cur, string path, vectorstring 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:vectorstring binaryTreePaths(TreeNode* root) {vectorstring result;string path;if (root NULL) return result;traversal(root, path, result);return result;} };如上代码精简了不少也隐藏了不少东西。 注意在函数定义的时候void traversal(TreeNode* cur, string path, vectorstring result) 定义的是string path每次都是复制赋值不用使用引用否则就无法做到回溯的效果。这里涉及到C语法知识 那么在如上代码中貌似没有看到回溯的逻辑其实不然回溯就隐藏在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(); // 回溯 - }整体代码如下 //版本二 class Solution { private:void traversal(TreeNode* cur, string path, vectorstring result) {path to_string(cur-val); // 中中为什么写在这里因为最后一个节点也要加入到path中if (cur-left NULL cur-right NULL) {result.push_back(path);return;}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(); // 回溯 -}}public:vectorstring binaryTreePaths(TreeNode* root) {vectorstring result;string path;if (root NULL) return result;traversal(root, path, result);return result;} }; 大家应该可以感受出来如果把 path -作为函数参数就是可以的因为并没有改变path的数值执行完递归函数之后path依然是之前的数值相当于回溯了 综合以上第二种递归的代码虽然精简但把很多重要的点隐藏在了代码细节里第一种递归写法虽然代码多一些但是把每一个逻辑处理都完整的展现出来了。 #拓展 这里讲解本题解的写法逻辑以及一些更具体的细节下面的讲解中涉及到C语法特性如果不是C的录友就可以不看了避免越看越晕。 如果是C的录友建议本题独立刷过两遍再看下面的讲解同样避免越看越晕造成不必要的负担。 在第二版本的代码中其实仅仅是回溯了 - 部分调用两次pop_back一个pop 一次pop-大家应该疑惑那么 path to_string(cur-val); 这一步为什么没有回溯呢 一条路径能持续加节点 不做回溯吗 其实关键还在于 参数使用的是 string path这里并没有加上引用 即本层递归中path 该节点数值但该层递归结束上一层path的数值并不会受到任何影响。 如图所示 节点4 的path在遍历到节点3path3遍历节点3的递归结束之后返回节点4回溯的过程path并不会把3加上。 所以这是参数中不带引用不做地址拷贝只做内容拷贝的效果。这里涉及到C引用方面的知识 在第一个版本中函数参数我就使用了引用即 vectorint path 这是会拷贝地址的所以 本层递归逻辑如果有path.push_back(cur-val); 就一定要有对应的 path.pop_back() 那有同学可能想为什么不去定义一个 string path 这样的函数参数呢然后也可能在递归函数中展现回溯的过程但关键在于path to_string(cur-val); 每次是加上一个数字这个数字如果是个位数那好说就调用一次path.pop_back()但如果是 十位数百位数千位数呢 百位数就要调用三次path.pop_back()才能实现对应的回溯操作这样代码实现就太冗余了。 所以第一个代码版本中我才使用 vector 类型的path这样方便给大家演示代码中回溯的操作。 vector类型的path不管 每次 路径收集的数字是几位数总之一定是int所以就一次 pop_back就可以。 #迭代法 至于非递归的方式我们可以依然可以使用前序遍历的迭代方式来模拟遍历路径的过程对该迭代方式不了解的同学可以看文章二叉树听说递归能做的栈也能做 (opens new window)和二叉树前中后序迭代方式统一写法 (opens new window)。 这里除了模拟递归需要一个栈同时还需要一个栈来存放对应的遍历路径。 C代码如下 class Solution { public:vectorstring binaryTreePaths(TreeNode* root) {stackTreeNode* treeSt;// 保存树的遍历节点stackstring pathSt; // 保存遍历路径的节点vectorstring 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的栈StackObject stack new Stack();这样就不用定义两个栈了都放到一个栈里就可以了。 #总结 本文我们开始初步涉及到了回溯很多同学过了这道题目可能都不知道自己其实使用了回溯回溯和递归都是相伴相生的。 我在第一版递归代码中把递归与回溯的细节都充分的展现了出来大家可以自己感受一下。 第二版递归代码对于初学者其实非常不友好代码看上去简单但是隐藏细节于无形。 最后我依然给出了迭代法。 对于本题充分了解递归与回溯的过程之后有精力的同学可以再去实现迭代法。
http://www.pierceye.com/news/934160/

相关文章:

  • 淘宝网网站建设出入南京最新通知今天
  • 做企业网站项目的心得做网站一定要后台嘛
  • 青峰集团响应式网站论坛网站怎么做跳转
  • 代网站建设制作微信公众号网站开发
  • 潍坊网站的公司电话网站关键字多少个
  • 网站开发确认书青岛做网站的公司哪个好
  • 权威的企业网站建设网站开发费用算无形资产吗
  • 网站安全在线检测网络制作软件
  • 潍坊网站设计制作威宁住房和城乡建设局网站
  • 哈尔滨企业建站网站开发推广文案怎么写
  • 建立电子商务网站目的想要开网店怎么开
  • iis怎么建设网站内容杭州做搜索引擎网站的公司
  • 兰州网站开发价格建设和住房保障部 网站
  • 机械设备网站建设效果好的徐州网站开发
  • 商丘网站建设专业现状桓台县网站建设
  • 一级a做爰片免费网站孕交视频教程wordpress用的什么前端
  • 中文设计网站wordpress退出登录
  • 贵阳网站建设多少钱?帝国cms资源网模板
  • 利津网站定制wordpress文章内容下载手机
  • 西宁网站搭建百度网站制作联系方式
  • 中铁建设集团门户网站登陆翻页h5制作软件
  • 在线教育网站建设策划中国哪里在大建设
  • 宁波网站制作首荐荣盛网络好网站后台发布了但看不见
  • 用asp做网站出现空白30天网站建设全程实录 pdf
  • 吉林3厅官齐聚任免大会宁波网站建设广州活动策划公司排名
  • 个人阿里云账号可以做网站备案ip网址域名查询网
  • 厦门的企业网站企业形象网站建设意义
  • 网站源码网网站建设 制作教程
  • 怎么样销售关于网站建设网站服务器过期了怎么办
  • 常州建网站需要多少钱直播app开发费用