1. 程式人生 > 其它 >二叉樹常見面試題(Go語言實現)

二叉樹常見面試題(Go語言實現)

開心一刻

語文詩歌題三大基本方法:詩人被貶法,詩人開心法,詩人思鄉法。
數學做題三大基本方法:肉眼觀察法,暴力代值法,顯然成立法。
英語閱讀題三大基本方法:硬幣投擲法,筆尖墜落指向法,投骰法。
物理做題三大基本方法:直接代公式法,保守少選法,一看就不對法。
政治大題三大基本方法:照搬材料原文法,照搬選擇題幹法,照搬選擇選項法。
歷史史論題三大基本方法:他說的有理法,他說的片面法,他說的扯淡法。
地理做題三大基本方法:看地圖法,畫地圖法,創造地圖法。

最近面試被問到二叉樹的相關問題, 有的沒有回答好, 趁此機會記錄一些網上常見的面試題目, 做些總結。

二叉樹的結構

type TreeNode struct {
    Val int
    Left *TreeNode
    Right *TreeNode
}

求二叉樹的最大深度

給定一個二叉樹,找出其最大深度。

二叉樹的深度為根節點到最遠葉子節點的最長路徑上的節點數。

說明: 葉子節點是指沒有子節點的節點。

演算法思路:節點為空時,返回節點深度為0;遞迴計算左右子樹的深度,然後用較大值加上父節點的深度1;然後返回,最終返回到根節點時即可求得最大深度。

func maxDepth(root *TreeNode) int {
    if root == nil {
        return 0
    }
    var left = maxDepth(root.Left)
    var right = maxDepth(root.Right)
    if left > right {
        return left + 1
    }
    return right + 1
}

求二叉樹的最小深度

給定一個二叉樹,找出其最小深度。

最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。

說明:葉子節點是指沒有子節點的節點。

演算法思路:根節點需要特殊判斷,為空時返回深度為0;對於葉子節點,不計算葉子節點的左右子樹深度,而是直接返回深度為1;對於只有左子樹的節點,左子樹深度標記為不可達,只有右子樹的節點同理;然後計算左右子樹的最小深度,用最小深度加上父節點的深度1

func minDepth(root *TreeNode) int {
    if root == nil {
        return 0
    }
    return getMin(root)
}

func getMin(root *TreeNode) int {
    if root == nil {
        return int(^uint(0) >> 1)
    }
    if root.Left == nil && root.Right == nil {
        return 1
    }
    return Min(getMin(root.Left), getMin(root.Right)) + 1
}

func Min(x, y int) int {
    if x > y {
        return y
    }
    return x
}

求二叉樹中節點的個數

演算法思路:遞迴計算左右子樹節點個數後,返回左右子樹節點個數和父節點個數的和即可

func numOfTreeNode(root *TreeNode) int {
    if root == nil {
        return 0
    }
    var left = numOfTreeNode(root.Left)
    var right = numOfTreeNode(root.Right)
    return left + right + 1
}

求二叉樹中葉子節點的個數

演算法思路:葉子節點需要單獨判斷,並返回結果1;然後遞迴計算左子樹葉子節點數量和右子樹葉子節點數量,然後相加並返回

func numOfNoChildNode(root *TreeNode) int {
    if root == nil {
        return 0
    }
    if root.Left == nil && root.Right == nil {
        return 1
    }
    var left = numOfTreeNode(root.Left)
    var right = numOfTreeNode(root.Right)
    return left + right
}

判斷二叉樹是否是平衡二叉樹

輸入一棵二叉樹的根節點,判斷該樹是不是平衡二叉樹。如果某二叉樹中任意節點的左右子樹的深度相差不超過1,那麼它就是一棵平衡二叉樹。
演算法思路:判斷是否是平衡二叉樹屬於求樹的最大深度的變種。當判斷某一節點的左右子樹的深度之差的絕對值大於1時,那麼整棵二叉樹就不是平衡二叉樹了,所以在此時可以返回一個特殊值來標記(此處用的-1,因為求二叉樹深度不可能出現負值),那麼最終的結果也會是這個特殊值,根據這個特殊值就可以判斷這個二叉樹是否是一個平衡二叉樹。

func isBalanced(root *TreeNode) bool {
    if root == nil {
        return true
    }
    return getDepth(root) != -1
}
func getDepth(root *TreeNode) int {
    if root == nil {
        return 0
    }
    var left = getDepth(root.Left)
    var right = getDepth(root.Right)
    if left == -1 || right == -1 || Abs(left, right) > 1 {
        return -1
    }
    if left > right {
        return left + 1
    } else {
        return right + 1
    }
}
func Abs(x, y int) int {
    if x > y {
        return x - y
    } else {
        return y - x
    }
}

求二叉樹中第K層節點的個數

與求葉子節點的數量相似, 只不過判斷葉子節點的時候修改成判斷當前k是否為1

func numOfKLevelNode(root *TreeNode, k int) int {
    if root == nil {
        return 0
    }
    if k == 1 {
        return 1
    }
    var left = numOfKLevelNode(root.Left, k - 1)
    var right = numOfKLevelNode(root.Right, k - 1)
    return left + right
}

判斷二叉樹是否是完全二叉樹

若設二叉樹的深度為 h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的結點都連續集中在最左邊,這就是完全二叉樹。(注:第 h 層可能包含 1~2^h個節點。)
根據完全二叉樹的定義, 使用層次遍歷即可解決.

func isCompleteTree(root *TreeNode) bool {
    if root == nil {
        return true
    }
    var queue = []*TreeNode{root}
    var num = 1
    for i := 0; len(queue) != 0; i++ {
        var q []*TreeNode
        var flag bool
        for j := 0; j < len(queue); j++ {
            node := queue[j]
            if flag && node.Left != nil {
                return false
            }
            if node.Left != nil {
                q = append(q, node.Left)
            } else {
                flag = true
            }
            if flag && node.Right != nil {
                return false
            }
            if node.Right != nil {
                q = append(q, node.Right)
            } else {
                flag = true
            }
        }
        if len(q) != 0 {
            if len(queue) != num {
                return false
            }
            num *= 2
        }
        queue = q
    }
    return true
}

兩個二叉樹是否完全相同

func isSameTree(p *TreeNode, q *TreeNode) bool {
    if p == nil && q == nil {
        return true
    }
    if p == nil || q == nil || p.Val != q.Val {
        return false
    }
    return isSameTree(p.Left, q.Left) && isSameTree(p.Right, q.Right)
}

兩個二叉樹是否互為映象

func isMirror(p *TreeNode, q *TreeNode) bool {
    if p == nil && q == nil {
        return true
    }
    if p == nil || q == nil || p.Val != q.Val {
        return false
    }
    return isMirror(p.Left, q.Right) && isSameTree(p.Right, q.Left)
}

翻轉二叉樹or映象二叉樹

func mirrorTree(root *TreeNode) *TreeNode {
    var dfs func(root *TreeNode)
    dfs = func(root *TreeNode) {
        if root == nil {
            return
        }
        dfs(root.Left)
        dfs(root.Right)
        root.Left, root.Right = root.Right, root.Left
    }
    dfs(root)
    return root
}

二叉樹的最近公共祖先

func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
    if root == nil {
        return nil
    }
    if root.Val == p.Val || root.Val == q.Val {
        return root
    }
    var left = lowestCommonAncestor(root.Left, p, q)
    var right = lowestCommonAncestor(root.Right, p, q)
    if left != nil && right != nil {
        return root
    }
    if left == nil {
        return right
    }
    return left
}

二叉樹的前序遍歷

func preorderTraversal(root *TreeNode) []int {
    var ret []int
    var preorder func(root *TreeNode) 
    preorder = func(root *TreeNode) {
        if root == nil {
            return
        }
        ret = append(ret, root.Val)
        preorder(root.Left)
        preorder(root.Right)
    }
    preorder(root)
    return ret
}
func preorderTraversal(root *TreeNode) []int {
    if root == nil {
        return nil
    }
    var ret []int
    var stack = []*TreeNode{root}
    for len(stack) != 0 {
        node := stack[len(stack) - 1]
        stack = stack[: len(stack) - 1]
        ret = append(ret, node.Val)
        if node.Right != nil {
            stack = append(stack, node.Right)
        }
        if node.Left != nil {
            stack = append(stack, node.Left)
        }
    }
    return ret
}

二叉樹的中序遍歷

func inorderTraversal(root *TreeNode) []int {
    var ret []int
    var inorder func(root *TreeNode) 
    inorder = func(root *TreeNode) {
        if root == nil {
            return
        }
        inorder(root.Left)
        ret = append(ret, root.Val)
        inorder(root.Right)
    }
    inorder(root)
    return ret
}
func inorderTraversal(root *TreeNode) []int {
    var (
        ret []int
        stack []*TreeNode
        cur *TreeNode
    )
    cur = root
    for len(stack) != 0 || cur != nil {
        for cur != nil {
            stack = append(stack, cur)
            cur = cur.Left
        }
        node := stack[len(stack) - 1]
        stack = stack[: len(stack) - 1]
        ret = append(ret, node.Val)
        cur = node.Right
    }
    return ret
}

二叉樹的後序遍歷

func postorderTraversal(root *TreeNode) []int {
    var ret []int
    var postorder func(root *TreeNode)
    postorder = func(root *TreeNode) {
        if root == nil {
            return
        }
        postorder(root.Left)
        postorder(root.Right)
        ret = append(ret, root.Val)
    }
    postorder(root)
    return ret
}
func postorderTraversal(root *TreeNode) []int {
    var (
        ret []int
        stack1 []*TreeNode
    )
    if root == nil {
        return nil
    }
    stack1 = append(stack1, root)
    for len(stack1) != 0 {
        node := stack1[len(stack1) - 1]
        stack1 = stack1[: len(stack1) - 1]
        ret = append(ret, node.Val)
        if node.Left != nil {
            stack1 = append(stack1, node.Left)
        }
        if node.Right != nil {
            stack1 = append(stack1, node.Right)
        }
    }
    for i, j := 0, len(ret) - 1; i < j; i, j = i + 1, j - 1 {
        ret[i], ret[j] = ret[j], ret[i]
    }
    return ret
}

二叉樹中路徑和為k的路徑

func pathSum(root *TreeNode, targetSum int) [][]int {
    var (
        ret [][]int
        path []int
    )
    var dfs func(root *TreeNode, target int) 
    dfs = func(root *TreeNode, target int) {
        if root == nil {
            return
        }
        if root.Left == nil && root.Right == nil {
            if root.Val == target {
                tmp := append(path, target)
                ret = append(ret, append([]int(nil), tmp...))
            }
            return
        }
        path = append(path, root.Val)
        dfs(root.Left, target - root.Val)
        dfs(root.Right, target - root.Val)
        path = path[: len(path) - 1]
    }
    dfs(root, targetSum)
    return ret
}

二叉樹層次遍歷

func levelOrder(root *TreeNode) [][]int {
    if root == nil {
        return nil
    }
    var queue = []*TreeNode{root}
    var ret [][]int
    for i := 0; len(queue) != 0; i++ {
        var q []*TreeNode
        ret = append(ret, []int(nil))
        for j := 0; j < len(queue); j++ {
            node := queue[j]
            ret[i] = append(ret[i], node.Val)
            if node.Left != nil {
                q = append(q, node.Left)
            }
            if node.Right != nil {
                q = append(q, node.Right)
            }
        }
        queue = q
    }
    return ret
}

判斷二叉樹是否是合法的二叉查詢樹

力扣連結

func isValidBST(root *TreeNode) bool {
    left, right := math.MinInt64, math.MaxInt64
    var in_order func(root *TreeNode, left, right int) bool 
    in_order = func(root *TreeNode, left, right int) bool {
        if root == nil {
            return true
        }
        if left >= root.Val || root.Val >= right {
            return false
        }
        return in_order(root.Left, left, root.Val) && in_order(root.Right, root.Val, right)
    }
    return in_order(root, left, right)
}
func isValidBST(root *TreeNode) bool {
    var (
        stack []*TreeNode
        cur *TreeNode
        pre int
    )
    if root == nil {
        return true
    }
    pre = -1 << 63
    cur = root
    for cur != nil || len(stack) != 0 {
        for cur != nil {
            stack = append(stack, cur)
            cur = cur.Left
        }
        node := stack[len(stack) - 1]
        stack = stack[: len(stack) - 1]
        cur = node.Right
        if pre >= node.Val {
            return false
        }
        pre = node.Val
    }
    return true
}