本期例题:
LeetCode 98. Validate Binary Search Tree 验证二叉搜索树(Medium)
LeetCode 426. Convert Binary Tree to Sorted Doubly Linked List 二叉树转化为链表(Medium)
本文将介绍二叉树问题中一个特殊的技巧:「在二叉树的前/中/后序遍历时对相邻结点进行操作」。这种方法不适用于大多数题目,但在一些特定的题目中使用这个技巧,能起到「秒杀」的效果。
还记得当年数据结构课上,老师对于二叉树的前序、中序、后序遍历的谆谆教诲吗?可能你一看到二叉树,前/中/后序三种遍历就在脑海中浮现出来。然而,当你开始在 LeetCode 上刷题,做了许多二叉树题目之后,就会发现,做这些题目似乎根本用不上什么前/中/后序遍历!
是的,求解二叉树问题最重要的思路是子问题思路,前面的几篇关于二叉树的文章一直在讨论的就是这种子问题思路。因为很多二叉树问题都是通过划分子问题,利用递归求解。对于这种子问题的思路来说,我们只是让左子树和右子树递归地完成计算任务,至于是左子树先计算还是右子树先计算,根本不重要,更不用说什么前/中/后序遍历了。
不过,二叉树的子问题思路并不是万能的。有些时候,用子问题来解题会比较麻烦。有时候题目具有特殊的性质,把前/中/后序遍历的思想掏出来会更加有效。本文就来讨论一下在什么时候适合用前/中/后序遍历的解题思路。
验证二叉搜索树:比较相邻结点
LeetCode 98. Validate Binary Search Tree(Medium)
给定一个二叉树,判断其是否是一个有效的二叉搜索树(BST)。二叉搜索树需要满足以下特征:
结点的左子树只包含小于当前结点的值;
结点的右子树只包含大于当前结点的值;
所有左子树和右子树自身也是二叉搜索树。
验证二叉搜索树这道题,其实完全可以用子问题的思路来做。我们可以定义三个子问题:「二叉树是否为 BST」、「二叉树的最小值」、「二叉树的最大值」。(至于为什么是这三个子问题,其实是有固定的套路的,详见上一篇文章:二叉树问题太复杂?「三步走」方法解决它!)对于每个子树而言,如果根结点的值小于左子树的最大值、或者大于右子树的最小值,那么就不满足二叉搜索树的性质。题解代码如下所示:
// 注意:此代码不完整,仅用于展示思路
boolean res;
public boolean isValidBST(TreeNode root) {
res = true;
traverse(root);
return res;
}
// 返回两个值
// 返回值0:二叉树的最小值
// 返回值1:二叉树的最大值
int[] traverse(TreeNode root) {
if (root == null) {
return ??; // 边界情况不好定义
}
int[] left = traverse(root.left);
int[] right = traverse(root.right);
if (root.val <= left[1] || root.val >= right[0])