本文正在参加「Java主题月 – Java 刷题打卡」,详情查看<活动链接>
【Java 刷题打卡】 刷题比玩游戏好多了,成就感越来越强,每天坚持刷几道题,每天锻炼30分钟,等8块腹肌,等大厂offer.
?
那就干吧! 这个专栏都是刷的题目都是关于二叉树的,我会由浅入深、循序渐进,刷题就是这样需要连续不断的记忆–艾宾浩斯记忆法2121112。二叉树的内容不多,但是都是每个程序员必备的,对了解红黑树、B+树、LSM树都非常有帮助等等
WAL+LSM-tree实现的leveldb和rocksdb
B+ 树的mysql
(HBASE) – LSM-tree的架构把random write转成sequential write,多层的compaction和lookup,存在写放大和读放大
TokuDB索引结构–Fractal Tree
还有更多,值得咱们发掘。
- 二叉树之中序遍历-迭代、递归
- 二叉树之前序遍历-迭代、递归
- 二叉树之后序遍历-迭代、递归
- 二叉树的层序遍历-迭代、递归
- 二叉树的最大深度-迭代、递归
- 二叉树之对称二叉树-迭代、递归
- 二叉树之路径总和-迭代、递归
- 从中序与后序遍历序列构造二叉树-迭代、递归
- 二叉树的序列化与反序列化-迭代、递归
- 验证二叉搜索树-迭代、递归
- 二叉搜索树中的搜索-迭代、递归
- 二叉搜索树中的插入操作-迭代、递归
- 删除二叉搜索树中的节点-迭代、递归
- 二叉搜索树的范围和-迭代、递归
- 二叉搜索树之最小高度树-迭代、递归
- 19. 二叉树之二叉树的镜像-迭代、递归
leecode 99. 恢复二叉搜索树
给你二叉搜索树的根节点 root ,该树中的两个节点被错误地交换。请在不改变其结构的情况下,恢复这棵树。
进阶:使用 O(n) 空间复杂度的解法很容易实现。你能想出一个只使用常数空间的解决方案吗?
示例 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 使二叉搜索树有效。
Morris 遍历算法整体步骤如下(假设当前遍历到的节点为 x):
-
如果 x 无左孩子,则访问 x 的右孩子,即 x = x.right。
-
如果 x 有左孩子,则找到 x 左子树上最右的节点(即左子树中序遍历的最后一个节点,x 在中序遍历中的前驱节点),我们记为 predecessor(前任)。根据predecessor 的右孩子是否为空,进行如下操作。
-
如果predecessor 的右孩子为空,则将其右孩子指向 x,然后访问 x 的左孩子,即 x = x.left。
-
如果 predecessor 的右孩子不为空,则此时其右孩子指向 x,说明我们已经遍历完 x 的左子树,我们将 predecessor 的右孩子置空,然后访问 x 的右孩子,即 x = x.right。
-
-
重复上述操作,直至访问完整棵树。
其实整个过程我们就多做一步:将当前节点左子树中最右边的节点指向它,这样在左子树遍历完成后我们通过这个指向走回了 x,且能再通过这个知晓我们已经遍历完成了左子树,而不用再通过栈来维护,省去了栈的空间复杂度。
了解完这个算法以后,其他地方与方法二并无不同,我们同样也是维护一个 pred 变量去比较即可,具体实现可以看下面的代码,这里不再赘述。
参考代码
定义一颗树
class TreeNode {
int val; // 头结点
TreeNode left; // 左子树
TreeNode right; // 右子树
TreeNode(int x) {
val = x;
}
}
// 测试方法
public static void main(String[] args) {
TreeNode treeNode = new TreeNode(1);
treeNode.left = new TreeNode(2);
treeNode.right = new TreeNode(3);
System.out.println("xxxx结果 = " + preorderTraversal(treeNode));
}
复制代码
JAVA Morris
class Solution {
public void recoverTree(TreeNode root) {
TreeNode x = null, y = null, pred = null, predecessor = null;
while (root != null) {
if (root.left != null) {
// predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止
predecessor = root.left;
while (predecessor.right != null && predecessor.right != root) {
predecessor = predecessor.right;
}
// 让 predecessor 的右指针指向 root,继续遍历左子树
if (predecessor.right == null) {
predecessor.right = root;
root = root.left;
}
// 说明左子树已经访问完了,我们需要断开链接
else {
if (pred != null && root.val < pred.val) {
y = root;
if (x == null) {
x = pred;
}
}
pred = root;
predecessor.right = null;
root = root.right;
}
}
// 如果没有左孩子,则直接访问右孩子
else {
if (pred != null && root.val < pred.val) {
y = root;
if (x == null) {
x = pred;
}
}
pred = root;
root = root.right;
}
}
swap(x, y);
}
public void swap(TreeNode x, TreeNode y) {
int tmp = x.val;
x.val = y.val;
y.val = tmp;
}
}
复制代码
真心感谢帅逼靓女们能看到这里,如果这个文章写得还不错,觉得有点东西的话
求点赞? 求关注❤️ 求分享? 对8块腹肌的我来说真的 非常有用!!!
如果本篇博客有任何错误,请批评指教,不胜感激 !❤️❤️❤️❤️