一、概述
二叉查找树 (Binary Search Tree,又叫二叉搜索树,二叉排序树) 它或者是一棵空树,或者是具有下列性质的 二叉树 :
- 若它的左子树不空,则左子树上所有结点的值均小于它的
根结点
的值。- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值。
- 它的左、右子树也分别为二叉排序树 。
二叉搜索树既有链表的快速插入与删除操作的特点,又有数组快速查找的优势。
二、常见算法
1. 验证二叉搜索树
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
解题思路:
利用中序遍历,实现左子节点与父节点比较和父节点与右子节点比较。如果左子节点的值大于等于父节点或父节点的值大于等于右子节点,则表示不满足二叉搜索树的定义。
这里需要利用一个临时变量存储上一个遍历到的节点值,用于跟下一个节点进行比较。
// 存储上一个访问到的节点值。
Integer pre = null;
public boolean isValidBST(TreeNode root) {
if (root == null) {
return true;
}
if (!isValidBST(root.left)) {
return false;
}
if (pre != null && pre >= root.val) {
return false;
}
// 记录当前节点,用于下一个节点值的比较。
pre = root.val;
return isValidBST(root.right);
}
2. 恢复二叉搜索树
给你二叉搜索树的根节点 root ,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树 。
解题思路:
这道题难点在于如何找到这两个需要交换的两个节点,并把他们换回来。
这里我们使用二叉树搜索树的非递归中序遍历来实现(中序遍历遍历元素是递增的),可以参考上一题的二叉搜索树验证的解题思路。因为不满足二叉搜索树必然存在前一个节点大于等于当前节点的特点。
如上图所示,中序遍历顺序是 [3, 2, 1],我们只要找到节点 3 和节点 1 交换顺序即可!
- 第一个节点查找:按照中序遍历时,前一个节点如果大于后一个节点,我们选取前一个节点,这里指节点3。
- 第二个节点查询:在第一个节点找到后,后面再次出现前一个节点大于后一个节点时,我们选择后一个节点,这里指节点 1。
public void recoverTree(TreeNode root) {
Stack<TreeNode> stack = new Stack();
TreeNode firstNode = null;
TreeNode secondNode = null;
// 借鉴校验二叉搜索树的思想,引入一个变量存储上一个节点的信息。。
TreeNode preNode = null;
while (root != null || !stack.isEmpty()) {
while (root != null) {
stack.push(root);
root = root.left;
}
if (!stack.isEmpty()) {
TreeNode pop = stack.pop();
// 第一个节点的查找
if (preNode != null && firstNode == null && preNode.val >= pop.val) {
firstNode = preNode;
}
// 第二个节点的查找
if (preNode != null && firstNode != null && preNode.val >= pop.val) {
secondNode = pop;
}
preNode = pop;
root = pop.right;
}
}
// 交换值就可以了。
int temp = firstNode.val;
firstNode.val = secondNode.val;
secondNode.val = temp;
}