刷題---樹篇---98. 驗證二叉搜索樹

98. 驗證二叉搜索樹

給定一個二叉樹,判斷其是否是一個有效的二叉搜索樹。

假設一個二叉搜索樹具有如下特徵:

節點的左子樹只包含小於當前節點的數。
節點的右子樹只包含大於當前節點的數。
所有左子樹和右子樹自身必須也是二叉搜索樹。
示例 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
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章