算法题 递增顺序搜索树

递增顺序搜索树

问题描述

给你一个二叉搜索树(BST)的根节点root,请将其重新排列为一棵只有右子节点的递增顺序搜索树。

要求

  • 树中每个节点没有左子节点
  • 只有右子节点
  • 节点按照中序遍历的顺序排列

返回新树的根节点。

示例

输入:[5,3,6,2,4,null,8,1,null,null,null,7,9]5/\36/\ \248//\179输出:[1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9]1\2\3\4\5\6\7\8\9

算法思路

中序遍历

  1. 核心思想

    • 二叉搜索树的中序遍历结果是递增序列
    • 按照中序遍历的顺序重新构建树,每个节点只有右子节点
  2. 方法

    • 方法一(递归):使用全局变量记录前一个节点,中序遍历时重新链接
    • 方法二(迭代):使用栈进行中序遍历,同时重新链接节点
    • 方法三(两次遍历):先中序遍历收集节点,再重新链接

代码实现

方法一:递归 + 全局变量

/** * Definition for a binary tree node. */classTreeNode{intval;TreeNodeleft;TreeNoderight;TreeNode(){}TreeNode(intval){this.val=val;}TreeNode(intval,TreeNodeleft,TreeNoderight){this.val=val;this.left=left;this.right=right;}}classSolution{/** * 递增顺序搜索树 - 递归 * * @param root 二叉搜索树的根节点 * @return 重新排列后的树的根节点 * * 算法思路: * 1. 使用虚拟头节点简化操作 * 2. 中序遍历过程中重新链接节点 * 3. 将每个节点的左子节点置为null */privateTreeNodeprev;// 记录中序遍历的前一个节点publicTreeNodeincreasingBST(TreeNoderoot){// 创建虚拟头节点TreeNodedummy=newTreeNode(0);prev=dummy;// 中序遍历并重新链接inorder(root);// 返回真正的根节点returndummy.right;}/** * 中序遍历并重新链接节点 * * @param node 当前节点 */privatevoidinorder(TreeNodenode){if(node==null){return;}// 递归处理左子树inorder(node.left);// 处理当前节点node.left=null;// 断开左子节点prev.right=node;// 将前一个节点的右指针指向当前节点prev=node;// 更新前一个节点// 递归处理右子树inorder(node.right);}}

方法二:迭代 + 栈

importjava.util.*;classSolution{/** * 递增顺序搜索树 - 迭代 */publicTreeNodeincreasingBST(TreeNoderoot){TreeNodedummy=newTreeNode(0);TreeNodeprev=dummy;Stack<TreeNode>stack=newStack<>();TreeNodecurrent=root;// 中序遍历的迭代实现while(current!=null||!stack.isEmpty()){// 一直向左走到底while(current!=null){stack.push(current);current=current.left;}// 处理栈顶节点current=stack.pop();current.left=null;// 断开左子节点prev.right=current;// 重新链接prev=current;// 更新前一个节点// 转向右子树current=current.right;}returndummy.right;}}

方法三:两次遍历

classSolution{/** * 两次遍历:先收集节点,再重新链接 */publicTreeNodeincreasingBST(TreeNoderoot){List<TreeNode>nodes=newArrayList<>();// 第一次遍历:中序遍历收集所有节点inorderCollect(root,nodes);// 第二次遍历:重新链接节点for(inti=0;i<nodes.size();i++){nodes.get(i).left=null;if(i<nodes.size()-1){nodes.get(i).right=nodes.get(i+1);}}returnnodes.isEmpty()?null:nodes.get(0);}privatevoidinorderCollect(TreeNodenode,List<TreeNode>nodes){if(node==null)return;inorderCollect(node.left,nodes);nodes.add(node);inorderCollect(node.right,nodes);}}

算法分析

  • 时间复杂度:O(n)

    • 每个节点被访问一次
    • 所有方法都是线性时间
  • 空间复杂度

    • 方法一(递归):O(h)
      • h 是树的高度,递归栈空间
      • 最坏情况(链状树):O(n)
      • 平均情况(平衡树):O(log n)
    • 方法二(迭代):O(h)
      • 显式栈空间
    • 方法三(两次遍历):O(n)
      • 额外存储所有节点的列表

算法过程

原树: [5,3,6,2,4,null,8,1,null,null,null,7,9] 中序遍历顺序: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 → 9 重新链接过程: dummy → 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 → 9

测试用例

importjava.util.*;publicclassTest{// 根据数组创建二叉树publicstaticTreeNodecreateTree(Integer[]arr){if(arr==null||arr.length==0||arr[0]==null){returnnull;}TreeNoderoot=newTreeNode(arr[0]);Queue<TreeNode>queue=newLinkedList<>();queue.offer(root);inti=1;while(!queue.isEmpty()&&i<arr.length){TreeNodenode=queue.poll();if(i<arr.length&&arr[i]!=null){node.left=newTreeNode(arr[i]);queue.offer(node.left);}i++;if(i<arr.length&&arr[i]!=null){node.right=newTreeNode(arr[i]);queue.offer(node.right);}i++;}returnroot;}// 将树转换为右链表数组publicstaticList<Integer>treeToRightList(TreeNoderoot){List<Integer>result=newArrayList<>();TreeNodecurrent=root;while(current!=null){result.add(current.val);current=current.right;}returnresult;}// 验证是否为有效的递增右链表publicstaticbooleanisValidIncreasingBST(TreeNoderoot){if(root==null)returntrue;TreeNodecurrent=root;Integerprev=null;while(current!=null){if(prev!=null&&current.val<=prev){returnfalse;}if(current.left!=null){returnfalse;// 左子节点必须为null}prev=current.val;current=current.right;}returntrue;}publicstaticvoidmain(String[]args){Solutionsolution=newSolution();// 测试用例1:标准示例Integer[]tree1={5,3,6,2,4,null,8,1,null,null,null,7,9};TreeNoderoot1=createTree(tree1);TreeNoderesult1=solution.increasingBST(root1);System.out.println("Test 1: "+treeToRightList(result1));// [1,2,3,4,5,6,7,8,9]System.out.println("Test 1 : "+isValidIncreasingBST(result1));// true// 测试用例2:单节点TreeNoderoot2=newTreeNode(1);TreeNoderesult2=solution.increasingBST(root2);System.out.println("Test 2: "+treeToRightList(result2));// [1]System.out.println("Test 2 : "+isValidIncreasingBST(result2));// true// 测试用例3:只有右子树Integer[]tree3={1,null,2,null,3};TreeNoderoot3=createTree(tree3);TreeNoderesult3=solution.increasingBST(root3);System.out.println("Test 3: "+treeToRightList(result3));// [1,2,3]System.out.println("Test 3 : "+isValidIncreasingBST(result3));// true// 测试用例4:只有左子树Integer[]tree4={3,2,null,1};TreeNoderoot4=createTree(tree4);TreeNoderesult4=solution.increasingBST(root4);System.out.println("Test 4: "+treeToRightList(result4));// [1,2,3]System.out.println("Test 4 : "+isValidIncreasingBST(result4));// true// 测试用例5:完全二叉搜索树Integer[]tree5={4,2,6,1,3,5,7};TreeNoderoot5=createTree(tree5);TreeNoderesult5=solution.increasingBST(root5);System.out.println("Test 5: "+treeToRightList(result5));// [1,2,3,4,5,6,7]System.out.println("Test 5 : "+isValidIncreasingBST(result5));// true// 测试用例6:空树TreeNoderesult6=solution.increasingBST(null);System.out.println("Test 6: "+(result6==null));// true// 测试用例7:两个节点(左)Integer[]tree7={2,1};TreeNoderoot7=createTree(tree7);TreeNoderesult7=solution.increasingBST(root7);System.out.println("Test 7: "+treeToRightList(result7));// [1,2]System.out.println("Test 7 : "+isValidIncreasingBST(result7));// true// 测试用例8:两个节点(右)Integer[]tree8={1,null,2};TreeNoderoot8=createTree(tree8);TreeNoderesult8=solution.increasingBST(root8);System.out.println("Test 8: "+treeToRightList(result8));// [1,2]System.out.println("Test 8 : "+isValidIncreasingBST(result8));// true// 测试用例9:大数值Integer[]tree9={100,50,150,25,75,125,175};TreeNoderoot9=createTree(tree9);TreeNoderesult9=solution.increasingBST(root9);System.out.println("Test 9: "+treeToRightList(result9));// [25,50,75,100,125,150,175]System.out.println("Test 9 : "+isValidIncreasingBST(result9));// true}}

关键点

  1. 中序遍历

    • 二叉搜索树的中序遍历天然就是递增序列
  2. 指针操作

    • 必须将每个节点的left置为null
    • 重新链接right指针形成链表
  3. 虚拟头节点

    • 简化边界处理,避免特殊处理第一个节点
    • 代码更加统一和简洁

常见问题

  1. 为什么不用创建新节点?

    • 要求"重新排列",重用原有节点
  2. 如何处理空树?

    • 空树的中序遍历为空,返回 null
    • 虚拟头节点的 right 为 null

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

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

相关文章

inline内联函数双重语义

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、先理清&#xff1a;inline的两个核心作用&#xff08;容易混淆&#xff09;二、头文件中写类成员函数的两种场景场景1&#xff1a;类体内部直接写函数实现&a…

导师严选2026 AI论文网站TOP10:本科生毕业论文写作全攻略

导师严选2026 AI论文网站TOP10&#xff1a;本科生毕业论文写作全攻略 2026年AI论文写作工具测评&#xff1a;为何需要一份权威榜单&#xff1f; 在人工智能技术不断渗透学术领域的今天&#xff0c;AI论文写作工具已成为本科生完成毕业论文的重要辅助。然而&#xff0c;面对市场…

容器开篇复制弱智问题答案

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录一、QVector/QMap 是否属于 STL&#xff1f;二、std::string&#xff1a;是否属于 STL&#xff1f;是否是容器&#xff1f;1. 是否属于 STL&#xff1f;2. 是否是容器…

AI+设计:用预置镜像快速构建创意辅助工具

AI设计&#xff1a;用预置镜像快速构建创意辅助工具 为什么设计工作室需要AI创意辅助工具 在设计行业&#xff0c;创意产出往往需要耗费大量时间和人力成本。传统设计流程中&#xff0c;从构思到成品需要反复修改&#xff0c;效率较低。而AI技术的出现&#xff0c;特别是图像生…

LLM动态调药糖尿病副作用砍半

&#x1f4dd; 博客主页&#xff1a;Jax的CSDN主页 LLM动态调药&#xff1a;糖尿病副作用减少50%的临床实践与未来展望目录LLM动态调药&#xff1a;糖尿病副作用减少50%的临床实践与未来展望 引言&#xff1a;糖尿病管理的痛点与技术拐点 一、糖尿病副作用的根源&#xff1a;为…

算法题 水果成篮

水果成篮 问题描述 你正在探访一家农场&#xff0c;农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示&#xff0c;其中 fruits[i] 是第 i 棵树产生的水果种类。 你有两个篮子&#xff0c;每个篮子只能装单一类型的水果&#xff0c;但你可以选择任意两棵树开始收集…

零成本体验:免费GPU资源+预装镜像玩转AI绘画

零成本体验&#xff1a;免费GPU资源预装镜像玩转AI绘画 作为一名对AI绘画感兴趣的大学生&#xff0c;你是否曾因高昂的GPU云服务费用和复杂的本地部署流程而望而却步&#xff1f;本文将介绍如何利用免费GPU资源和预装镜像&#xff0c;零门槛体验Stable Diffusion等AI绘画技术&a…

科哥手把手教学:1小时掌握Z-Image-Turbo二次开发

科哥手把手教学&#xff1a;1小时掌握Z-Image-Turbo二次开发 作为一名全栈开发者&#xff0c;当我接到集成Z-Image-Turbo的任务时&#xff0c;内心是有些忐忑的。虽然我对传统开发流程轻车熟路&#xff0c;但AI模型开发领域对我来说还是一片未知的领域。幸运的是&#xff0c;经…

技术创业者必看:低成本搭建AI图像生成SaaS

技术创业者必看&#xff1a;低成本搭建AI图像生成SaaS 为什么选择AI图像生成SaaS&#xff1f; 作为一名技术创业者&#xff0c;你可能已经注意到AI图像生成技术的巨大潜力。从营销海报到产品设计&#xff0c;Stable Diffusion等开源模型正在改变内容创作的方式。但传统部署方案…

API开发速成:基于预配置Z-Image-Turbo环境快速构建图像生成服务

API开发速成&#xff1a;基于预配置Z-Image-Turbo环境快速构建图像生成服务 作为一名后端工程师&#xff0c;当你接到任务要将Z-Image-Turbo集成到公司系统时&#xff0c;可能会对AI模型部署感到陌生。本文将介绍如何利用预配置的Z-Image-Turbo环境快速构建图像生成API服务&…

等保二级与三级深度解析及对比分析

在数字化时代,网络安全等级保护制度已成为企业保障信息安全的重要合规手段。等保二级和三级作为常见的保护级别,在适用场景、技术要求和管理措施等方面存在显著差异。本文将对等保二级和三级进行详细解析,并对比分析两者的核心差异,为企业选择合适的等保级别提供参考。 一…

产品经理必备:10分钟了解AI图像生成技术

产品经理必备&#xff1a;10分钟了解AI图像生成技术 作为一名非技术背景的产品经理&#xff0c;你可能经常听到"Stable Diffusion"、"AI绘图"这些热词&#xff0c;但面对复杂的安装配置和GPU需求&#xff0c;往往无从下手。本文将带你快速理解AI图像生成的…

亲测好用!9款AI论文软件测评:本科生毕业论文全攻略

亲测好用&#xff01;9款AI论文软件测评&#xff1a;本科生毕业论文全攻略 AI论文软件测评&#xff1a;为什么你需要一份靠谱的工具推荐 随着人工智能技术的不断进步&#xff0c;越来越多的本科生开始借助AI工具辅助毕业论文写作。然而&#xff0c;面对市场上琳琅满目的AI论文软…

揭秘Z-Image-Turbo超快推理:预配置镜像+云端GPU实战指南

揭秘Z-Image-Turbo超快推理&#xff1a;预配置镜像云端GPU实战指南 如果你正在寻找一种能够快速生成高质量图像的AI解决方案&#xff0c;Z-Image-Turbo绝对值得关注。这款由阿里巴巴通义MAI团队开发的图像生成模型&#xff0c;通过创新的8步蒸馏技术&#xff0c;在保持照片级质…

STM32嵌入式:如何使用VSCode EIDE来获取flash块数据并转换成可视化的数据 来判断源头数据是否错误

STM32嵌入式&#xff1a;如何使用VSCode EIDE来获取flash块数据并转换成可视化的数据 来判断源头数据是否错误 VSCode 里 EIDE 本身没有像 Keil 那样“直接导出 Flash 到文件”的按钮。但你已经在用 EIDE Cortex-Debug 调试&#xff0c;所以可以用调试后端&#xff08;OpenOCD…

算法题 最小差值 I

908. 最小差值 I 问题描述 给你一个整数数组 nums 和一个整数 k。你可以选择数组中的任一元素并将其替换为 [num - k, num k] 范围内的任意整数。 在应用此操作至多一次后&#xff0c;求数组中最大值和最小值之间的最小可能差值。 示例&#xff1a; 输入: nums [1], k 0 输出…

告别CUDA报错:预装镜像带你轻松玩转Z-Image-Turbo

告别CUDA报错&#xff1a;预装镜像带你轻松玩转Z-Image-Turbo 作为一名计算机专业的学生&#xff0c;在课程项目中需要使用AI生成图像时&#xff0c;你是否曾被各种依赖包冲突和CUDA版本问题困扰得焦头烂额&#xff1f;本文将介绍如何通过预装好的Z-Image-Turbo镜像&#xff0c…

玩转AI绘画:周末用云端GPU打造个人艺术展

玩转AI绘画&#xff1a;周末用云端GPU打造个人艺术展 作为一名艺术爱好者&#xff0c;你是否曾想过举办一场属于自己的AI艺术展&#xff1f;借助Stable Diffusion等开源AI绘画工具&#xff0c;现在完全可以在云端GPU环境下快速生成高质量的艺术作品。本文将手把手教你如何利用预…

简析:一种名为 ObjectSense 的编程语言

让我们通过以下三个维度来了解它&#xff1a;1. 语言本质与起源 基础平台&#xff1a;它是一种基于 Vim Script (VimL) 进行面向对象封装的脚本编程语言。核心特性&#xff1a;高度精炼&#xff0c;核心代码仅在千行之内。设计初衷&#xff1a;旨在让开发者能像写 Python 一样简…

使用MATLAB绘制3D心形图和玫瑰花图案

以下是两种不同的实现方法&#xff1a; 1. 3D心形图 方法一&#xff1a;参数方程心形 % 3D心形图 - 参数方程方法 figure(Position, [100, 100, 1200, 500]);% 子图1&#xff1a;参数方程心形 subplot(1,2,1); % 创建网格 [u, v] meshgrid(linspace(0, 2*pi, 50), linspace(0,…