[Go数据结构与算法]3. 二叉树的结构与实现

110 阅读3分钟

ChatGPT Image 2026年1月8日 10_33_47.png

二叉树的定义

二叉树是一种常见的数据结构,区别于一般的线性表,它有一个节点保存了指向其他两个节点的指针。对于它指向的两个节点,同时也保存了指向了其他两个节点的指针。

二叉树节点的定义

Val 字段保存了数据,Left 和 Right 字段分别表示指向根节点的左右子节点的指针

// 二叉树节点  
type TreeNode[T any] struct {  
    Val T  
    Left *TreeNode[T]  
    Right *TreeNode[T]  
}

二叉树的遍历

二叉树的遍历通常分为先序遍历中序遍历后序遍历。先序遍历表示,一棵二叉树按照根、左、右的顺序遍历。先遍历根节点,然后再遍历左子树,最后遍历右子树。子树按照上述顺序递归遍历。

先序遍历

// 先序遍历  
func preOrderTraverse[T any](root *TreeNode[T]) {  
    if root == nil {  
        return  
    }  
    fmt.Print(root.Val, " ")  
    preOrderTraverse(root.Left)  
    preOrderTraverse(root.Right)  
}

中序遍历

// 中序遍历  
func inOrderTraverse[T any](root *TreeNode[T]) {  
    if root == nil {  
        return  
    }  
    inOrderTraverse(root.Left)  
    fmt.Print(root.Val, " ")  
    inOrderTraverse(root.Right)  
}

后序遍历

// 后序遍历  
func postOrderTraverse[T any](root *TreeNode[T]) {  
    if root == nil {  
        return  
    }  
    postOrderTraverse(root.Left)  
    postOrderTraverse(root.Right)  
    fmt.Print(root.Val, " ")  
}

层次遍历

func levelOrderTraverse[T any](root *TreeNode[T]) [][]T {  
    ans := make([][]T, 0)  
    if root == nil {  
        return ans  
    }  
    q := queue.NewArrayQueue[*TreeNode[T]]()  
    q.Enqueue(root)  
    for !q.IsEmpty() {  
        l := make([]T, 0)  
        n := q.Size()  
        for i := 0; i < n; i++ {  
            temp := q.Dequeue()  
            l = append(l, temp.Val)  
            if temp.Left != nil {  
                q.Enqueue(temp.Left)  
            }  
            if temp.Right != nil {  
                q.Enqueue(temp.Right)
            }
        }
        ans = append(ans, l)
    }  
    return ans
}

二叉树的序列化和构建

二叉树的先序序列化

// 按照先序遍历序列化  
func preOrderString[T any](t *TreeNode[T]) []string {  
    var ans []string  
    var dfs func(node *TreeNode[T])  
    dfs = func(node *TreeNode[T]) {  
        if node == nil {  
            ans = append(ans, EmptyNode)  
        } else {  
            ans = append(ans, fmt.Sprintf("%v", node.Val))  
            dfs(node.Left)  
            dfs(node.Right)  
        }  
    }  
    dfs(t)  
    return ans  
}

二叉树的先序序列构建

// 按照先序遍历顺序构建二叉树  
func CreateTreeByPreOrder(order []string) *TreeNode[int] {  
    index := 0  
    var dfs func(q []string, i *int) *TreeNode[int]  
    dfs = func(q []string, i *int) *TreeNode[int] {  
        var root *TreeNode[int]  
        if *i < len(q) {  
            cur := q[*i]  
            *i = *i + 1  
            if cur == EmptyNode {  
                return nil  
            } else {  
                num, _ := strconv.Atoi(cur)  
                root = &TreeNode[int]{Val: num}  
                root.Left = dfs(q, i)  
                root.Right = dfs(q, i)  
            }  
        }  
        return root  
    }  
    return dfs(order, &index)  
}

经典例题

判断是否是搜索二叉树

// 判断是否是二叉搜索树 使用中序遍历判断  
func isBST[T any](root *TreeNode[T], comparator util.Comparator[T]) bool {  
    list := make([]*TreeNode[T], 0)  
    var dfs func(r *TreeNode[T], l []*TreeNode[T])  
    dfs = func(r *TreeNode[T], l []*TreeNode[T]) {  
        if r == nil {  
        return  
        }  
        dfs(r.Left, l)  
        list = append(list, r)  
        dfs(r.Right, l)  
    }  
    dfs(root, list)  

    for i := 1; i < len(list); i++ {  
        if comparator(list[i-1].Val, list[i].Val) >= 0 {  
            return false  
        }  
    }  
    return false  
}

判断是否是完全二叉树

// 判断是否是完全二叉树  
func isCompleteBinaryTree[T any](root *TreeNode[T]) bool {  
    // 空二叉树算一棵完全二叉树  
    if root == nil {  
        return true  
    }  
  
    // 目前为止是否发现左右孩子不双全的节点  
    // 如果发现了, 后续节点都必须是叶节点  
    flag := false  
  
    // 宽度优先遍历  
    q := queue.NewArrayQueue[*TreeNode[T]]()  
    q.Enqueue(root)  
    for !q.IsEmpty() {  
        node := q.Dequeue()  
        if (node.Right != nil && node.Left == nil) || (flag && (node.Left != nil || node.Right != nil)) {  
            return false  
        }  
        if node.Left != nil {  
            q.Enqueue(node.Left)  
        }  
        if node.Right != nil {  
            q.Enqueue(node.Right)  
        }  
  
        // 发现了左右节点存在不满的情况  
        if node.Left == nil || node.Right == nil {  
            flag = true  
        }  
    }  
    return true  
}

判断是否存在从根节点到叶子结点和等于targetSum

func hasPathSum(root *TreeNode[int], targetSum int) bool {  
    if root == nil {  
        return false  
    }  
    existed := false  
    var dfs func(node *TreeNode[int], preSum, targetSum int)  
    dfs = func(node *TreeNode[int], preSum, targetSum int) {  
        // 是叶子节点  
        if node.Left == nil && node.Right == nil {  
            existed = existed || preSum+node.Val == targetSum  
        } else {  
            if node.Left != nil {  
                dfs(node.Left, preSum+node.Val, targetSum)  
            }  
            if node.Right != nil {  
                dfs(node.Right, preSum+node.Val, targetSum)  
            }  
        }  
    }  
    dfs(root, 0, targetSum)  
    return existed  
}