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

剑指Offer(数据结构与算法面试题精讲)C++版——day17

剑指Offer(数据结构与算法面试题精讲)C++版——day17

      • 题目一:节点值之和最大的路径
      • 题目二:展平二叉搜索树
      • 题目三:二叉搜索树的下一个节点
      • 附录:源码gitee仓库

题目一:节点值之和最大的路径

    题目:在二叉树中将路径定义为顺着节点之间的连接从任意一个节点开始到达任意一个节点所经过的所有节点。路径中至少包含一个节点,不一定经过二叉树的根节点,也不一定经过叶节点。给定非空的一棵二叉树,请求出二叉树所有路径上节点值之和的最大值。例如,在如图8.6所示的二叉树中,从节点15开始经过节点20到达节点7的路径的节点值之和为42,是节点值之和最大的路径。题目:如图8.3所示,请设计一个算法将二叉树序列化成一个字符串,并能将该字符串反序列化出原来二叉树的算法。
    根据示例可以得到,这里的最大和路径可以从左侧节点绕过父节点再走到右侧。回顾前面剑指Offer(数据结构与算法面试题精讲)C++版——day16中的题目三(向下的路径节点值之和),因为需要遍历从根节点遍历到所有的叶子节点,整体上需要满足一种自上而下的结构,而这里需要的考虑绕过根节点,于是可以想到将前序遍历改成后序遍历,保证这里的路径能够从左到右绕过父节点探索。
    但是,这样显然不够,因为如果简单使用后续遍历会发现,15,7这种路径不能够存在,因为没有直接节点相连,并且如果选择了15,20,7这样的路径,那么不能够再选择当前20节点的父节点,因为这种结构存在路径重复,与题意不符。于是想到我们对于一个新节点的遍历,应该考虑如下三种情况:
(1)沿着该节点向左孩子遍历;
(2)沿着该节点向右孩子遍历;
(3)舍弃前面的遍历结果,考虑左孩子->父节点->右节点遍历;
    最终得到如下代码:

int maxSum = INT_MIN;
int dfs(treeNode* tree) {if (tree == nullptr){return 0;}int leftMax = max(0, dfs(tree->left));//若左右分支返回的值为负数,则对路径和做负贡献,直接舍弃int rightMax = max(0, dfs(tree->right));int leftAndRight = leftMax + tree->val + rightMax; //可能为left->root->right,使用了后续不可再延伸 maxSum = max(maxSum, leftAndRight);return tree->val + max(leftMax, rightMax);//向node的父结点返回经过node的单边分支的最大路径和
}int maxPathSum(treeNode* tree) { dfs(tree);return maxSum;
}

在这里插入图片描述

    这道题的代码其实不是很好想,在leetcode上面这是一道hard题,我这里也是看了很多题解介绍才搞明白的。为了方便理解这里将maxSum设置成全局变量,在实际算法题中其实是可行的(如果不太习惯这种写法那其实也可以将maxSum设置成一个引用类型对象,本质上差不多)。在理解时可以先不考虑“左根右”这种遍历方式,整个代码就更方便理解了,一开始分别计算左右子树的和,如果左子树大接下来进入左子树,右子树大进入右子树,因为如果子树更小,那不如直接取另外一边,之所以深度递归下去是为了检测是否还有负数值产生干扰以提前裁剪。这样遍历的整体形式就是自上而下了,为了考虑到“左根右”遍历,再补上这样计算与总和的大小比较取max,这样整个代码就清晰很多了。

题目二:展平二叉搜索树

    题目:给定一棵二叉搜索树,请调整节点的指针使每个节点都没有左子节点。调整之后的树看起来像一个链表,但仍然是二叉搜索树。例如,把图8.8(a)中的二叉搜索树按照这个规则展平之后的结果如图8.8(b)所示。
在这里插入图片描述
    由题意可知这是一种中序遍历方式,只是需要维护节点不能够具有左子树。这里有一种暴力思路,先按照中序遍历的方式遍历这棵二叉搜索树,遍历完将节点值压入到队列中,然后删除节点,之后利用队列中的值重新构建一棵二叉树。但这里还有一种更好的方式,不需要删除树和重建,而是采用栈存储节点,然后再调整指针指向,因为涉及到修改指针指向,所以这里应该使用中序遍历的非递归写法(非递归写法思路见第三题),然后利用临时指针记录前一个指针,实现逆序构建指针指向,最终得到代码如下:

treeNode * flattenTree(treeNode* tree) {treeNode* current=tree,*pre=nullptr,*root=nullptr;stack<treeNode* > st;while(current||!st.empty()) {while(current) {//找到最左边元素st.push(current);current=current->left;}current=st.top();st.pop();if(pre) {//检查节点是否第一个出现,记录根节点并修改指向 pre->right=current;} else {root=current;}pre=current; current->left=nullptr;current=current->right;}return root;
}

在这里插入图片描述

题目三:二叉搜索树的下一个节点

    题目:给定一棵二叉搜索树和它的一个节点p,请找出按中序遍历的顺序该节点p的下一个节点。假设二叉搜索树中节点的值都是唯一的。例如,在图8.9的二叉搜索树中,节点8的下一个节点是节点9,节点11的下一个节点是null。
在这里插入图片描述

    结合前面第二题,这里就比较简单了,同样是利用中序遍历的非递归写法,由于第二题没有详细说明二叉树中序遍历的非递归写法思路,这里刚好补上。我们在中序遍历时,首先需要拿到最左下角元素,然后遍历“根节点”,再来遍历右子树,在右子树中还是需要先找到右子树的最左侧节点,这就是为什么中序遍历的非递归写法中会出现两个while循环。然后对于中间的“根节点”使用栈来维护,因为最左侧的节点先一步出栈,之后是这些中间“根节点”,所以在第二个while中会提前将current节点入栈,如果拿到最后一个比如这里的节点5,其后没有节点了,那么在栈中取节点,这时就回到了中间“根节点”6,之后换入到右子树,按照前面的步骤接着进行。这里你可能想到可能current->right为空,比如这里7节点扫描完成之后,接下来访问的节点就是空节点了,这里就巧妙利用了第一层while的判定条件,只要current为空但栈中有节点那么弹出栈中元素,保证了整个中序遍历的完整性。

treeNode * getNextNode(treeNode* tree,int val) {treeNode* current=tree,*pre=nullptr;stack<treeNode* > st;while(current||!st.empty()) {while(current) {//找到最左边元素st.push(current);current=current->left;}current=st.top();st.pop();if(pre&&pre->val==val) {//取出二叉搜索树的下一个节点return current;}pre=current;current=current->right;}return nullptr;
}

在这里插入图片描述

附录:源码gitee仓库

    考虑到有些算法需要模拟数据,如果全部放进来代码显得过长,不利于聚焦在算法的核心思路上。于是把所有的代码整理到了开源仓库上,如果想要看详细模拟数据,可在开源仓库自取:【凌空暗羽/剑指Offer-C++版手写代码】。

    我是【Jerry说前后端】,本系列精心挑选的算法题目均基于经典的《剑指 Offer(数据结构与算法面试题精讲)》。在如今竞争激烈的技术求职环境下,算法能力已成为前端开发岗位笔试考核的关键要点。通过深入钻研这一系列算法题,大家能够系统地积累算法知识和解题经验。每一道题目的分析与解答过程,都像是一把钥匙,为大家打开一扇通往高效编程思维的大门,帮助大家逐步提升自己在数据结构运用、算法设计与优化等方面的能力。
    无论是即将踏入职场的应届毕业生,还是想要进一步提升自身技术水平的在职开发者,掌握扎实的算法知识都是提升竞争力的有力武器。希望大家能跟随我的步伐,在这个系列中不断学习、不断进步,为即将到来的前端笔试做好充分准备,顺利拿下心仪的工作机会!快来订阅吧,让我们一起开启这段算法学习之旅!

相关文章:

  • [c语言日寄]免费文档生成器——Doxygen在c语言程序中的使用
  • 特伦斯便携钢琴V20有哪些优势
  • [预备知识]1. 线性代数基础
  • 4月21日星期一今日早报简报微语报早读
  • 颠覆传统!毫秒级响应的跨平台文件同步革命,远程访问如本地操作般丝滑
  • FTTR 全屋光纤架构分享
  • 【原创】Ubuntu20.04 安装 Isaac Gym 仿真器
  • 视频分析设备平台EasyCVR安防视频小知识:安防监控常见故障精准排查方法
  • init_tcicb函数有调用,但snapshot函数没调用
  • Spring AOP优化in查询,性能提升巨大
  • Unreal如何使用后处理材质实现一个黑屏渐变效果
  • Linux常见指令介绍中(入门级)
  • VSCode远程图形化GDB
  • 【React】获取元素距离页面顶部的距离
  • 多维度信息捕捉:利用向量、稀疏向量、全文搜索及张量实现RAG的极致性能
  • 随机面试--<二>
  • 可穿戴无线生理信号采集贴片产品市场需求简析
  • DevOps功能详解
  • 第 3 篇:揭秘时间模式 - 时间序列分解
  • 【显卡占用】kill程序后,显卡仍被占用
  • 国羽3比0横扫日本晋级苏迪曼杯决赛,将战韩国与印尼胜者
  • 人民日报:上海“模速空间”何以汇聚超百家大模型企业
  • 乌方公布矿产协议详情:未提债务义务,包含美再援助条款
  • 金砖国家外长会晤落幕,外交部:发出了反对单边霸凌行径的“金砖声音”
  • 长三角议事厅·周报|长三角游戏出海,关键在“生态输出”
  • 2025上海体育消费节启动,多形式联动打造体育消费盛宴