1 题目介绍
1.1 题目介绍
给你二叉搜索树的根节点 root
,该树中的两个节点被错误地交换。请在不改变其结构的情况下,恢复这棵树。
**进阶:**使用 O(n) 空间复杂度的解法很容易实现。你能想出一个只使用常数空间的解决方案吗?
实例一
输入:root = [1,3,null,null,2]
输出:[3,1,null,null,2]
解释:3 不能是 1 左孩子,因为 3 > 1 。交换 1 和 3 使二叉搜索树有效。
题目来源:leetcode-cn.com/problems/re…
1.2 题目分析
在分析之前,先介绍一个概念:前驱节点,二叉搜索树的前驱节点指中序遍历的前一个节点;
二叉搜索树中序遍历的结果是一个递增的数组;正常的二叉搜索树中序遍历结果如下图
交换中序遍历前驱节点中序遍历如下图所示
交换其他节点中序遍历如下图所示
以上分析得出以下结果
- 第1个错误节点:第一个逆序对中的较大节点
- 第2个错误节点:最后一个逆序对中的较小节点;
- 交换两个错误节点的值即可恢复二叉搜索树;
2 中序遍历寻找逆序对实现
采用递归方法进行中序遍历寻找逆序对;
var recoverTree = function(root) {
let pre = null;
let first = null;
let second = null;
findWrongNodes(root);
// 交换两个错误节点值
let tmp = first.val;
first.val = second.val;
second.val = tmp;
function findWrongNodes(root) {
// 中序遍历寻找逆序对
if(root == null) return;
findWrongNodes(root.left);
find(root);
findWrongNodes(root.right);
}
function find(node) {
// 出现了逆序对
if(pre !== null && pre.val > node.val) {
// 第二个错误的节点,最后一个逆序对中较小的那个节点
second = node;
// 第一个错误节点,第一个逆序对中较大的那个节点
if(first != null) return;
first = pre;
}
pre = node;
}
};
时间复杂度:O(n);
空间复杂度:O(n);
3 Morris遍历寻找逆序对实现
使用Morris方法遍历二叉树,可以实现时间复杂度O(n),空间复杂度O(1);
3.1 Morris遍历介绍
Morris遍历是一种常数空间的遍历方法,其本质是线索二叉树(Threaded Binary Tree);Morris遍历是利用树的叶子节点左右孩子为空(存在大量空闲指针),实现缩减空间开销;Morris中序遍历是指在遍历过程中,通过利用叶子节点空的right指针,指向中序遍历的后继节点,从而避免了对栈的依赖;实现空间复杂的降为O(1); 下面介绍二叉搜索树中序遍历的Morris遍历;
3.2 分析
执行步骤
假设遍历到当前节点是N
- 如果N.left != null,找到N的前驱节点P;
a,如果P.right == null; P.right = N, N = N.left; 回到步骤1
b,如果P.right == N; P.right = null, 此时N为中序遍历的节点,N = N.right, 回到步骤1; - 如果N.left == null;即找到中序遍历的节点,N = N.right,回到步骤1
- 重复步骤1,2直到N == null;
3.3 代码实现
var recoverTree = function(root) {
let pre = null;
let first = null;
let second = null;
while(root != null) {
if(root.left != null) {
let pred = root.left;
while(pred.right !== null && pred.right != root) {
pred = pred.right;
}
if(pred.right == null) {
pred.right = root;
root = root.left;
} else {
find(root);
pred.right = null;
root = root.right;
}
} else {
find(root);
root = root.right;
}
}
let tmp = first.val;
first.val = second.val;
second.val = tmp;
function find(node) {
// 出现了逆序对
if(pre !== null && pre.val > node.val) {
// 第二个错误的节点,最后一个逆序对中较小的那个节点
second = node;
// 第一个错误节点,第一个逆序对中较大的那个节点
if(first != null) return;
first = pre;
}
pre = node;
}
};
时间复杂度:O(n);
空间复杂度:O(1);
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!