題目:驗證二叉搜索樹
難度:Medium
題目內容:
Given a binary tree, determine if it is a valid binary search tree (BST).
Assume a BST is defined as follows:
- The left subtree of a node contains only nodes with keys less than the node's key.
- The right subtree of a node contains only nodes with keys greater than the node's key.
- Both the left and right subtrees must also be binary search trees.
翻譯:
給定一棵二叉樹,確定它是否是一個有效的二叉搜索樹(BST)。
BST的定義如下:
節點的左子樹只包含小於節點鍵的鍵節點。
節點的右子樹只包含大於節點鍵的鍵節點。
左和右子樹都必須是二叉搜索樹。
Example 1:
Input: 2 / \ 1 3 Output: true
Example 2:
5 / \ 1 4 / \ 3 6 Output: false
我的思路:搜索二叉樹的驗證就是要求每一個子樹都是滿足搜索樹的“左小右大”的規定,
1、先判斷自己作為根節點的左右二叉是否符合;
2、然后返回左右節點的遞歸結果的 “與” (全都符合才算符合)
我的代碼:
1 public boolean isValidBST(TreeNode root) { 2 if (root == null) { 3 return true; 4 } 5 6 if (root.left != null) { 7 TreeNode cur = root.left; 8 while (cur.right != null) { 9 cur = cur.right; 10 } 11 if (cur.val >= root.val) { 12 return false; 13 } 14 } 15 16 if (root.right != null) { 17 TreeNode cur = root.right; 18 while (cur.left != null) { 19 cur = cur.left; 20 } 21 if (cur.val <= root.val) { 22 return false; 23 } 24 } 25 26 return isValidBST(root.left) && isValidBST(root.right); 27 }
我的復雜度:O(N*logN)
編碼過程中的問題:
1、沒仔細看題,而且記錯搜索樹的定義了,當出現兩個值相等的時候,此時不是搜索樹;
2、最開始只考慮到判斷根節點和左右兩個子節點就行了,結果后面案例跑錯了,例如:
5 / \ 1 6 / \ 3 7 每個子樹都是正確的二叉搜索樹,但是整體上看,5的右子樹內有比它小的3,所以此樹不是二叉搜索樹。
此時想到二叉樹刪除算法,當刪除節點有兩個子節點的時候,此時會選擇此節點左節點的最右子系節點,或者右節點的最左子系節點進行代替,所以這兩個節點值才是最接近根節點值的節點,所以每次的單個子樹判斷應該判斷這兩個,而不是左右子節點就行了。
答案代碼:
1 public boolean isValidBST(TreeNode root) { 2 return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE); 3 } 4 5 public boolean isValidBST(TreeNode root, long minVal, long maxVal) { 6 if (root == null) return true; 7 if (root.val >= maxVal || root.val <= minVal) return false; 8 return isValidBST(root.left, minVal, root.val) && isValidBST(root.right, root.val, maxVal); 9 }
答案復雜度:O(N)
答案思路:
也是利用了遞歸的思想,分別對每一個子樹進行判斷,但是它的亮點在於在判斷的時候並不需要對子樹進行搜索“最相近的值”,而是利用了“最大值”、“最小值”的思想:
對於每個子樹,都有一個最大值和一個最小值,
對於左子樹,最大值就是它的根節點的值,最小值是根節點的最小值(左父親或者MIN_VALUE)
對於右子樹,最小值就是它的根節點的值,最大值是根節點的最大值(右父親或者MAX_VALUE)
例如:
5 / \ 1 6 / \ 3 7
5的滿足小於最大值,大於最小值,然后遞歸(1,MIN,5) && 遞歸(4,5,MAX)
。。。
3節點的最大值為6,最小值應該為5,此時不滿足,所以return false
其實還有一種非遞歸的解法:中序遍歷,利用二叉搜索樹中序遍歷的有序性(在中序遍歷的出棧時判斷此值是否小於之前出棧的那個節點的值)
1 public boolean isValidBST(TreeNode root) { 2 if (root == null) return true; 3 Stack<TreeNode> stack = new Stack<>(); 4 TreeNode pre = null; 5 while (root != null || !stack.isEmpty()) { 6 while (root != null) { 7 stack.push(root); 8 root = root.left; 9 } 10 root = stack.pop(); 11 if(pre != null && root.val <= pre.val) return false; 12 pre = root; 13 root = root.right; 14 } 15 return true; 16 }
注意:不能使用出棧值與棧頂進行比較,因為在中序遍歷的過程中棧頂可能為空,所以此時無法比較。