递增顺序搜索树
问题描述
给你一个二叉搜索树(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算法思路
中序遍历:
核心思想:
- 二叉搜索树的中序遍历结果是递增序列
- 按照中序遍历的顺序重新构建树,每个节点只有右子节点
方法:
- 方法一(递归):使用全局变量记录前一个节点,中序遍历时重新链接
- 方法二(迭代):使用栈进行中序遍历,同时重新链接节点
- 方法三(两次遍历):先中序遍历收集节点,再重新链接
代码实现
方法一:递归 + 全局变量
/** * 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)
- 额外存储所有节点的列表
- 方法一(递归):O(h)
算法过程
原树: [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&¤t.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}}关键点
中序遍历:
- 二叉搜索树的中序遍历天然就是递增序列
指针操作:
- 必须将每个节点的
left置为null - 重新链接
right指针形成链表
- 必须将每个节点的
虚拟头节点:
- 简化边界处理,避免特殊处理第一个节点
- 代码更加统一和简洁
常见问题
为什么不用创建新节点?
- 要求"重新排列",重用原有节点
如何处理空树?
- 空树的中序遍历为空,返回 null
- 虚拟头节点的 right 为 null