題目:給定一棵樹,判斷是否是合法的二叉搜索樹。
如果合法返回true,不合法則返回false。
首先,應該明確二叉搜索樹的定義,二叉搜索樹是一個遞歸的定義,即二叉搜索樹的左子節點小於根節點,而右節點大於根節點,同時左右子樹也是一棵二叉搜索樹。
錯誤的想法:
先判斷根節點和左右子節點的大小關係,如果不符合大小關係,直接返回false。如果符合,則遞歸調用判斷左右子樹是否合法。
如下:
/**
* 驗證是否是合法二叉搜索樹
*/
public boolean isValidBST(TreeNode root) {
if (root == null) {
return true;
}
// 判斷根節點與左子節點的關係
boolean isLeftValid = true;
if (root.left != null) {
isLeftValid = root.val > root.left.val;
}
// 判斷根節點與右子節點的關係
boolean isRightValid = true;
if (root.right != null) {
isRightValid = root.val < root.right.val;
}
// 左子點及右子節點遞歸調用,判斷是否符合這種大小關係
return isLeftValid && isRightValid && isValidBST(root.left) && isValidBST(root.right);
}
這種想法只考慮了根節點與其左右子節點的關係,即只考慮了每三個節點之前的大小關係,而沒有考慮子節點與所有剩餘子節點的大小關係。
如對於如下的二叉樹則會得到錯誤結果:
4
1 6
0 2 3 7
這棵二叉樹很顯然不是二叉搜索樹,因爲其中的3小於4。
正確的解法一
/**
* 驗證是否是合法二叉搜索樹
*/
public boolean isValidBST(TreeNode root) {
return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
/**
* 當前root值的上下界要在指定的範圍內
* @param root
* @param lower 下界
* @param upper 上界
* @return
*/
public boolean isValidBST(TreeNode root, long lower, long upper) {
if (root == null) {
return true;
}
// 不符合搜索樹的定義
if (root.val <= lower || root.val >= upper) {
return false;
}
return isValidBST(root.left, lower, root.val) && isValidBST(root.right, root.val, upper);
}
正確的解法二:
// 用long型的數據來記錄前一個節點,int型過不了leetcode測試用例
long pre = Long.MIN_VALUE;
/**
* @param root
* @return 本質上還是利用了中序的排序性質,只是用了遞歸的方式。
*/
public boolean isValidBST(TreeNode root) {
if (root == null) {
return true;
}
// 先遞歸判斷左子樹是否合法
if (!isValidBST(root.left)) {
return false;
}
// 在判斷當前節點與左子節點的大小關係
if (root.val <= pre) {
return false;
}
pre = root.val;
// 在判斷右子樹是否合法
return isValidBST(root.right);
}
正確的解法三:
利用二叉搜索樹的中序遍歷有序性,邊遍歷邊比較。
/**
* 在遍歷的過程中比較, 在leetcode上這種方式的時間還沒有上面的遞歸調用快。
*/
public boolean isValidBST3(TreeNode root) {
if (root == null) {
return true;
}
Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
TreeNode cur = root;
// stack是空的,故加入cur不爲空的判斷
while (!stack.isEmpty() || cur != null) {
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
TreeNode tmp = stack.pop();
if (tmp.val <= pre) {
return false;
} else {
pre = tmp.val;
}
cur = tmp.right;
// 這裏不能在push了,因爲cur在while循環開頭就會push,如果這裏push就會push兩次了。
// if (cur != null) {
// stack.push(cur);
// }
}
return true;
}