給定一個二叉樹,判斷其是否是一個有效的二叉搜索樹。
假設一個二叉搜索樹具有如下特徵:
節點的左子樹只包含小於當前節點的數。
節點的右子樹只包含大於當前節點的數。
所有左子樹和右子樹自身必須也是二叉搜索樹。
示例 1:
輸入:
2
/ \
1 3
輸出: true
示例 2:
輸入:
5
/ \
1 4
/ \
3 6
輸出: false
解釋: 輸入爲: [5,1,4,null,null,3,6]。
根節點的值爲 5 ,但是其右子節點值爲 4 。
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/validate-binary-search-tree
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。
_____________________________________________________________________________________________________
驗證二叉搜索樹,主要是理解bst的性質,左<根<右
方法一:
所以,根據這條性質,中序遍歷是一個很好的驗證bst的方法,x0<x1<x2...<xn
代碼如下:
func isValidBST(root *TreeNode) bool {
//中序遍歷驗證bts
if root == nil {
return true
}
m := make([]int,0)
count := 0
res := true
Run(root,&m,&count,&res)
return res
}
func Run(root *TreeNode, m *[]int,max *int,flag *bool) {
if root == nil || *flag == false {
return
}
Run(root.Left,m,max,flag)
if len(*m) == 0 {
*m = append(*m,root.Val)
*max = root.Val
} else {
if *max >= root.Val {
*flag = false
}
*m = append(*m,root.Val)
*max = root.Val
}
Run(root.Right,m,max,flag)
}
使用一個m數組存放中序遍歷的結果,flag作爲標記,也可以進行剪枝處理。
觀察上述代碼可以發現,雖然利用了m數組存放所有遍歷的結果,但是實際上相比較的就是倒數第一和倒數第二個節點的值,於是可以考慮在空間上進行優化,只用一個遍歷存儲前一個節點的值。
代碼如下:
func isValidBST(root *TreeNode) bool {
//中序遍歷驗證bts
if root == nil {
return true
}
count := 0
res := true
max := 0
Run(root,&count,&max,&res)
return res
}
func Run(root *TreeNode, count *int,max *int,flag *bool) {
if root == nil || *flag == false {
return
}
Run(root.Left,count,max,flag)
if *count == 0 {
*max = root.Val
*count++
} else {
if *max >= root.Val {
*flag = false
}
*max = root.Val
}
Run(root.Right,count,max,flag)
}
也可用一種更“遞歸”的思路,即不用額外的變量flag進行標記結果,僅用返回值向上傳遞結果。但是實際上我認爲,只要是方法容易理解、記憶,能夠效率較高的解決問題,都是好方法。
代碼如下:
func isValidBST(root *TreeNode) bool {
if root == nil {
return true
}
k := -9999999
res := Run(root,&k)
return res
}
func Run(root *TreeNode,k *int) bool {
if root == nil {
return true
}
l := true
r := true
if root.Left != nil {
l = Run(root.Left,k)
}
if *k == -9999999 {
*k = root.Val
} else {
if *k >= root.Val {
return false
}
*k = root.Val
}
if root.Right != nil {
r = Run(root.Right,k)
}
return l && r
}
此時就不用flag標記和傳遞結果,直接利用返回值傳遞結果。
方法二:
利用bst的性質,根節點總是比左子樹的所有值都大,比右子樹的所有值都小。
於是可以考慮,用兩個變量 up 和 low 分別表示節點所在子樹的最大值和最小值。
下一步向右遍歷時,保留up,更新low爲當前節點;
下一步向左遍歷時,保留low,更新up爲當前節點.
代碼如下:
func isValidBST(root *TreeNode) bool {
return Run(root,nil,nil)
}
func Run(root , lower , upper *TreeNode) bool {
if root == nil {
return true
}
if lower != nil && root.Val <= lower.Val {
return false
}
if upper != nil && root.Val >= upper.Val {
return false
}
if !Run(root.Right,root,upper) {
return false
}
if !Run(root.Left,lower,root) {
return false
}
return true
}