递归-深度优先遍历-99. 恢复二叉搜索树

本文介绍了一种通过中序遍历找到并交换二叉搜索树中两个错误节点的方法,确保树的有效性。针对不同错误节点位置的情况进行了详细讨论。

Leetcode 恢复二叉搜索树
在这里插入图片描述

分析:
首先题目寿命恰好存在两个错误节点;因为二叉搜索树的中序遍历一定是有序的,那么原有问题就转化为该有序的数组,一定存在两个点是错误的,我们只需要找出这两个点,并且交换对应的值就可以解决这个问题。

那么我们要如何找到这两个错误位置呢?它会存在如下三种情况:

  • 情况1: 这两个错误的位置在左子树
  • 情况2: 这两个错误的位置在右子树
  • 情况3:这两个错误的位置横叉在两边

根据这种方式:我们按照如下的思路进行题解编码:
(1) 首先按照递归深度优先遍历,将二叉树转为List数组
(2) 找出根节点的索引位置
(3) 声明LLeft,LRight,RLeft,RRight,分别代表左子树的待交换的值,以及右子树待交换的值。根据根节点索引计算出这四个值出来
(4)针对这4个值得逻辑判断:

if (LLeft != -1 && RRight != -1) {
                swap(LLeft,RRight, nodesList);
            } else if (LLeft != -1 && RRight == -1) {
                swap(LLeft,LRight,nodesList);
            } else if (RRight != -1 && LLeft == -1) {
                swap(RLeft,RRight, nodesList);
            }

那么最终的代码实现如下:

/**
 * 题目Id:99
 * 题目:恢复二叉搜索树
 * 内容: //给你二叉搜索树的根节点 root ,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树 。
 * //
 * //
 * //
 * // 示例 1:
 * //
 * //
 * //输入:root = [1,3,null,null,2]
 * //输出:[3,1,null,null,2]
 * //解释:3 不能是 1 的左孩子,因为 3 > 1 。交换 1 和 3 使二叉搜索树有效。
 * //
 * //
 * // 示例 2:
 * //
 * //
 * //输入:root = [3,1,4,null,null,2]
 * //输出:[2,1,4,null,null,3]
 * //解释:2 不能在 3 的右子树中,因为 2 < 3 。交换 2 和 3 使二叉搜索树有效。
 * //
 * //
 * //
 * // 提示:
 * //
 * //
 * // 树上节点的数目在范围 [2, 1000] 内
 * // -2³¹ <= Node.val <= 2³¹ - 1
 * //
 * //
 * //
 * //
 * // 进阶:使用 O(n) 空间复杂度的解法很容易实现。你能想出一个只使用 O(1) 空间的解决方案吗?
 * // Related Topics 树 深度优先搜索 二叉搜索树 二叉树 👍 771 👎 0
 * <p>
 * 日期:2022-08-28 21:54:42
 */
//给你二叉搜索树的根节点 root ,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树 。
//
//
//
// 示例 1:
//
//
//输入:root = [1,3,null,null,2]
//输出:[3,1,null,null,2]
//解释:3 不能是 1 的左孩子,因为 3 > 1 。交换 1 和 3 使二叉搜索树有效。
//
//
// 示例 2:
//
//
//输入:root = [3,1,4,null,null,2]
//输出:[2,1,4,null,null,3]
//解释:2 不能在 3 的右子树中,因为 2 < 3 。交换 2 和 3 使二叉搜索树有效。
//
//
//
// 提示:
//
//
// 树上节点的数目在范围 [2, 1000] 内
// -2³¹ <= Node.val <= 2³¹ - 1
//
//
//
//
// 进阶:使用 O(n) 空间复杂度的解法很容易实现。你能想出一个只使用 O(1) 空间的解决方案吗?
// Related Topics 树 深度优先搜索 二叉搜索树 二叉树 👍 771 👎 0

package leetcode.editor.cn;

import common.TreeNode;

import java.util.ArrayList;
import java.util.List;

public class P99RecoverBinarySearchTreeV2 {
    public static void main(String[] args) {
        Solution solution = new P99RecoverBinarySearchTreeV2().new Solution();
        System.out.println("Hello world");
    }
    //leetcode submit region begin(Prohibit modification and deletion)

    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode() {}
     *     TreeNode(int val) { this.val = val; }
     *     TreeNode(int val, TreeNode left, TreeNode right) {
     *         this.val = val;
     *         this.left = left;
     *         this.right = right;
     *     }
     * }
     */
    class Solution {
        public void recoverTree(TreeNode root) {
            List<TreeNode> nodesList = new ArrayList<>();
            traversalNodeTree(root, nodesList);
            int rootIndex = -1;
            for (int i = 0; i < nodesList.size(); i++) {
                if (nodesList.get(i).val == root.val) {
                    rootIndex = i;
                    break;
                }
            }
            int LLeft = -1;
            int LRight = rootIndex;
            for (int i = 0; i < rootIndex; i++) {
                if (nodesList.get(i).val > nodesList.get(i + 1).val) {
                    LLeft = i;
                    break;
                }
            }
            for (int j = rootIndex; j >= 1; j--) {
                if (nodesList.get(j).val < nodesList.get(j - 1).val) {
                    LRight = j;
                    break;
                }
            }
            int RLeft = rootIndex;
            int RRight = -1;
            for (int i = rootIndex; i < nodesList.size() - 1; i++) {
                if (nodesList.get(i).val > nodesList.get(i + 1).val) {
                    RLeft = i;
                    break;
                }
            }
            for (int j = nodesList.size() - 1; j >= rootIndex + 1; j--) {
                if (nodesList.get(j).val < nodesList.get(j - 1).val) {
                    RRight = j;
                    break;
                }
            }
            if (LLeft != -1 && RRight != -1) {
                swap(LLeft,RRight, nodesList);
            } else if (LLeft != -1 && RRight == -1) {
                swap(LLeft,LRight,nodesList);
            } else if (RRight != -1 && LLeft == -1) {
                swap(RLeft,RRight, nodesList);
            }
        }

        private void swap(int leftNotValidIndex, int rightNotValidIndex, List<TreeNode> nodesList) {
            int temp = nodesList.get(leftNotValidIndex).val;
            nodesList.get(leftNotValidIndex).val = nodesList.get(rightNotValidIndex).val;
            nodesList.get(rightNotValidIndex).val = temp;
        }


        private void traversalNodeTree(TreeNode root, List<TreeNode> nodesList) {
            if (root == null) {
                return;
            }
            if (root.left != null) {
                traversalNodeTree(root.left, nodesList);
            }
            nodesList.add(root);

            if (root.right != null) {
                traversalNodeTree(root.right, nodesList);
            }
        }
    }
//leetcode submit region end(Prohibit modification and deletion)

}
# 力扣hot100刷题记录表 ### 一,哈希部分 - [ ] 1. 两数之和 (简单) - [ ] 2. 字母异位词分组(中等) - [ ] 3. 最长连续序列(中等) ### 二,双指针部分 - [ ] 4. 移动零(简单) - [ ] 5. 盛水最多的容器 (中等) - [ ] 6. 三数之和 (中等) - [ ] 7. 接雨水(困难) ### 三,滑动窗口 - [ ] 8. 无重复字符的最长子串(中等) - [ ] 9. 找到字符中所有的字母异位词(中等) ### 四,子串 - [ ] 10. 和为k的子数组(中等) - [ ] 11. 滑动窗口最大值(困难) - [ ] 12. 最小覆盖子窜(困难) ### 五,普通数组 - [ ] 13. 最大子数组和(中等) - [ ] 14. 合并区间(中等) - [ ] 15. 轮转数组(中等) - [ ] 16. 除自身以外数组的乘积(中等) - [ ] 17. 缺失的第一个正数(困难) ### 六,矩阵 - [ ] 18. 矩阵置零(中等) - [ ] 19. 螺旋矩阵 (中等) - [ ] 20. 旋转图像 (中等) - [ ] 21. 搜索二维矩阵Ⅱ (中等) ### 七,链表 - [ ] 22. 相交链表 (简单) - [ ] 23. 反转链表 (简单) - [ ] 24. 回文链表 (简单) - [ ] 25. 环形链表 (简单) - [ ] 26. 环形链表Ⅱ (中等) - [ ] 27. 合并两个有序链表 (简单) - [ ] 28. 两数相加 (中等) - [ ] 29. 删除链表的倒数第 N 个结点 (中等) - [ ] 30. 两两交换链表中的节点 (中等) - [ ] 31. K个一组翻转链表 (困难) - [ ] 32. 随机链表的复制 (中等) - [ ] 33. 排序链表 (中等) - [ ] 34. 合并 K 个升序链表 (困难) - [ ] 35. LRU 缓存 (中等) ### 八,二叉树 - [ ] 36. 二叉树的中序遍历 (简单) - [ ] 37. 二叉树的最大深度 (简单) - [ ] 38. 翻转二叉树 (简单) - [ ] 39. 对称二叉树 (简单) - [ ] 40. 二叉树的直径 (简单) - [ ] 41. 二叉树的层序遍历 (中等) - [ ] 42. 将有序数组转换为二叉搜索树 (简单) - [ ] 43. 验证二叉搜索树 (中等) - [ ] 44. 二叉搜索树中第 K 小的元素 (中等) - [ ] 45. 二叉树的右视图 (中等) - [ ] 46. 二叉树展开为链表 (中等) - [ ] 47. 从前序与中序遍历序列构造二叉树 (中等) - [ ] 48. 路径总和 III (中等) - [ ] 49. 二叉树的最近公共祖先 (中等) - [ ] 50. 二叉树中的最大路径和 (困难) ### 九,图论 - [ ] 51. 岛屿数量 (中等) - [ ] 52. 腐烂的橘子 (中等) - [ ] 53. 课程表 (中等) - [ ] 54. 实现 Trie(前缀树) (中等) ### 十,回溯 - [ ] 55.全排列(中等) - [ ] 56.子集(中等) - [ ] 57.电话号码的字母组合(中等) - [ ] 58.组合总和(中等) - [ ] 59.括号生成(中等) - [ ] 60.单词搜索(中等) - [ ] 61.分割回文串(中等) - [ ] 62.N 皇后 (困难) ### 十一,二分查找 - [ ] 63. 搜索插入位置 (简单) - [ ] 64. 搜索二维矩阵 (中等) - [ ] 65. 在排序数组中查找元素的第一个和最后一个位置 (中等) - [ ] 66. 搜索旋转排序数组 (中等) - [ ] 67. 寻找旋转排序数组中的最小值 (中等) - [ ] 68. 寻找两个正序数组的中位数 (困难) ### 十二,栈 - [ ] 69. 有效的括号 (简单) - [ ] 70. 最小栈 (中等) - [ ] 71. 字符串解码 (中等) - [ ] 72. 每日温度 (中等) - [ ] 73. 柱状图中最大的矩形 (困难) ### 十三,堆 - [ ] 74. 数组中的第K个最大元素 (中等) - [ ] 75. 前K 个高频元素 (中等) - [ ] 76. 数据流的中位数 (闲难) ### 十四,贪心算法 - [ ] 77. 买卖股票的最佳时机 (简单) - [ ] 78. 跳跃游戏 (中等) - [ ] 79. 跳跃游戏 III (中等) - [ ] 80. 划分字母区间 (中等) ### 十五,动态规划 - [ ] 81. 爬楼梯(简单) - [ ] 82. 杨辉三角 (简单) - [ ] 83. 打家劫舍 (中等) - [ ] 84. 完全平方数 (中等) - [ ] 85. 零钱兑换 (中等) - [ ] 86. 单词拆分 (中等) - [ ] 87. 最长递增子序列 (中等) - [ ] 88. 乘积最大子数组 (中等) ### 十六,多维动态规划 - [ ] 91. 不同路径 (中等) - [ ] 92. 最小路径和 (中等) - [ ] 93. 最长回文子串 (中等) - [ ] 94. 最长公共子序列 (中等) - [ ] 95. 编辑距离 (中等) ### 十七,技巧 - [ ] 96. 只出现一次的数字 (简单) - [ ] 97. 多数元素 (简单) - [ ] 98. 颜色分类 (中等) - [ ] 99. 下一个排列 (中等) - [ ] 100. 寻找重复数 (中等) 如何使用
07-20
为了高效地使用 LeetCode Hot 100 题目列表进行刷题和提升算法能力,可以采取以下几个策略: ### 1. 制定合理的刷题计划 设定一个每日或每周的刷题目标,例如每天解决 2-3 道题目。这样可以避免过度压力,同时保持持续的进步。优先选择与自己当前技能水平匹配的题目,逐步挑战更高难度的问题。 ### 2. 深入理解每道题目 不要仅仅满足于通过题目,而是要深入理解解题思路和背后的算法原理。例如,在解决类似单词拆分(Word Break)问题时,可以使用动态规划的方法,将问题分解为子问题进行求解[^2]。代码示例如下: ```python def wordBreak(s, wordDict): word_set = set(wordDict) n = len(s) dp = [False] * (n + 1) dp[0] = True # 空字符串可以被拆分 for i in range(1, n + 1): for j in range(i): if dp[j] and s[j:i] in word_set: dp[i] = True break return dp[n] ``` ### 3. 多种方法解决问题 尝试使用不同的方法解决同一道题目,例如递归、动态规划、贪心算法等。这有助于提高解决问题的灵活性和深度理解不同算法的适用场景。例如,在解决汉诺塔问题时,可以通过递归思想实现[^3]。 ### 4. 复盘与总结 每次完成一道题目后,花时间总结解题思路和技巧。记录下自己的学习笔记,例如常见的算法模板、易错点以及优化思路。这有助于形成系统的知识体系。 ### 5. 结合官方题解和其他资源 参考 LeetCode 官方题解和其他高质量的学习资源,了解最优解和不同解法的时间复杂度分析。这可以拓宽思路,帮助找到更高效的解决方案。 ### 6. 参与讨论与分享 加入 LeetCode 讨论区或相关学习社区,与其他刷题者交流解题思路。分享自己的解法并学习他人的经验,可以快速提升自己的算法水平。 ### 7. 使用分类刷题法 LeetCode Hot 100 题目涵盖多个算法类别,如数组、字符串、动态规划、树等。按类别集中刷题有助于系统性地掌握某一类问题的解法。例如,先集中解决所有与动态规划相关的题目,再转向其他类别。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值