二叉树的定义
二叉树是一种常见的数据结构,区别于一般的线性表,它有一个节点保存了指向其他两个节点的指针。对于它指向的两个节点,同时也保存了指向了其他两个节点的指针。
二叉树节点的定义
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
}