LeetCode897 递增顺序搜索树 & 剑指OfferII 052 展平二叉搜索树
题目
解题
二叉搜索树的增序排列就是中序遍历的顺序。
LeetCode426 将二叉搜索树转化为排序的双向链表 & 剑指Offer 36 二叉搜索树与双向链表 的简单版,426 题需要构造的 双向循环链表,本题是单向链表,相当于 left 不用指向前驱节点,直接指向 null 就行,而且最后首尾相连的一步也不用。
具体的思路不重复了,直接指路 426 题,一些注意点都标明出来啦!
其实有好几道将二叉树转换为链表的题目,本质上是中序/前序/后序遍历。
- 可以将节点按顺序放入一个数组中,最后去修改 left 和 right 指针
- 也可以原地修改,但是 要特别注意的是 left 指针和 right 指针什么时候可以修改:比如前序遍历的递归写法,如果在处理 root 的左子树根节点时,把 root.right 指向左子树,那么右子树的记录便丢掉了(LeetCode114 二叉树展开为链表)。再例如本题:因为在处理 root 根节点时,左子树已经处理完毕,left 指针可以置为 null,但倘若 visit 函数写成下面这样,就会出现环:
// javascript
const visit = (cur) => {
if (head === null) {
head = cur;
}
if (tail !== null) {
tail.left = null; // 等到下一个节点加入时才来改变 tail 的 left
tail.right = cur;
}
tail = cur;
};
[2,1,4,null,null,3]
最后一个节点是节点4,节点4后面没有节点加入,所以它的left指针没有机会被置为null
所以节点4的left指针一直指向节点3。而节点3的后继节点是节点4,节点3的right指向节点4
于是就有了环
因为不同的解题思路的处理流程不一样,所以也不能一概而论地讲出一个结论来,还是要拿着图分析一遍再来敲代码,敲完代码后再拿着图去检验一下。
在 visit 的函数里有判断 head 和 tail 是否为 null 的语句,官方用了链表常用的方法:设置了哑节点,可以省去对边界情况的讨论。
解题一:递归
// javascript
var increasingBST = function(root) {
const dfs = (root) => {
if (root === null) return;
dfs(root.left);
visit(root);
dfs(root.right);
};
const visit = (cur) => {
if (head === null) {
head = cur;
}
if (tail !== null) {
tail.right = cur;
cur.left = null;
}
tail = cur;
};
let head = null, tail = null;
dfs(root);
return head;
};
解题二:栈
// javascript
var increasingBST = function(root) {
const visit = (cur) => {
if (head === null) {
head = cur;
}
if (tail !== null) {
tail.right = cur;
cur.left = null;
}
tail = cur;
};
let head = null, tail = null;
const stk = new Array();
while (root !== null || stk.length > 0) {
while (root !== null) {
stk.push(root);
root = root.left;
}
root = stk.pop();
visit(root);
root = root.right;
}
return head;
};
解题三:Morris 中序遍历
// javascript
var increasingBST = function(root) {
const visit = (cur) => {
if (head === null) {
head = cur;
}
if (tail !== null) {
tail.right = cur;
cur.left = null;
}
tail = cur;
};
let head = null, tail = null;
while (root !== null) {
if (root.left !== null) {
let predecessor = root.left;
while (predecessor.right !== null && predecessor.right !== root) {
predecessor = predecessor.right;
}
if (predecessor.right === null) {
predecessor.right = root;
root = root.left;
} else if (predecessor.right === root) {
// 再次强调一下顺序丫!
predecessor.right = null;
visit(root);
root = root.right;
}
} else {
visit(root);
root = root.right;
}
}
return head;
};