【代码随想录——二叉树一周目】

1. 二叉树理论

二叉树(Binary tree)是树形结构的一个重要类型。二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成
长这样的结构
在这里插入图片描述

##1.1 二叉树的种类

  • 满二叉树
    • 定义:深度为k,有 2 k − 1 2^k-1 2k1个节点的二叉树
  • 完全二叉树
    • 定义:深度为k,有n个节点的二叉树当且仅当其每一个节点都与深度为k的满二叉树中编号从1到n的节点一一对应时,称为完全二叉树。
    • 特点叶子节点只可能出现在层序最大的两层上,并且某个节点的左分支下子孙的最大层序与右分支下子孙的最大层序相等或大1
  • 二叉搜索树
    • 二叉搜索树是一个有序树。
    • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
    • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
    • 它的左、右子树也分别为二叉排序树
  • 平衡二叉搜索树
    • AVL(Adelson-Velsky and Landis)树
    • 性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵二叉平衡树。
      在这里插入图片描述

1.2 二叉树的存储方式

二叉树可以链式存储,也可以顺序存储。

那么链式存储方式就用指针, 顺序存储的方式就是用数组。

顾名思义就是顺序存储的元素在内存是连续分布的,而链式存储则是通过指针把分布在各个地址的节点串联一起。
在这里插入图片描述
在这里插入图片描述
用数组来存储二叉树如何遍历的呢?
如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。

1.3 二叉树的遍历方式

  • 深度优先遍历
    • 前序遍历(递归法、迭代法)
    • 中序遍历(递归法、迭代法)
    • 后续遍历(递归法、迭代法)
  • 广度优先遍历
    • 层次遍历(迭代法)

1.4 基本的代码实现

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

2. 二叉树的递归遍历

递归函数的三要素

  • 确定递归函数的参数和返回值
    • 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
  • 确定终止条件
    • 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
  • 确定单层递归的逻辑
    • 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

3.1 二叉树的前序遍历

func preorderTraversal(root *TreeNode) []int {
	res := make([]int, 0, 100)
	preorder(root, &res)
	return res
}
// 这里要注意了,一定要使用*[]int,因为本质上切片也是一个struct,如果不使用这个的话相当于创建了一个新的切片。
func preorder(root *TreeNode, res *[]int) {
	if root == nil {
		return
	}
	*res = append(*res, root.Val)
	preorder(root.Left, res)
	preorder(root.Right, res)
}

3.2 二叉树的后序遍历

func postorderTraversal(root *TreeNode) []int {
	res := make([]int, 0, 100)
	postorder(root, &res)
	return res
}

// 这里要注意了,一定要使用*[]int,因为本质上切片也是一个struct,如果不使用这个的话相当于创建了一个新的切片。
func postorder(root *TreeNode, res *[]int) {
	if root == nil {
		return
	}
	postorder(root.Left, res)
	postorder(root.Right, res)
    *res = append(*res, root.Val)
}

3.3 二叉树的中序遍历

func inorderTraversal(root *TreeNode) []int {
	res := make([]int, 0, 100)
	inorder(root, &res)
	return res
}

// 这里要注意了,一定要使用*[]int,因为本质上切片也是一个struct,如果不使用这个的话相当于创建了一个新的切片。
func inorder(root *TreeNode, res *[]int) {
	if root == nil {
		return
	}
	inorder(root.Left, res)
	*res = append(*res, root.Val)
	inorder(root.Right, res)
}

3. 二叉树的迭代遍历

3.1 二叉树的前序遍历

前序遍历是中左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。

为什么要先加入 右孩子,再加入左孩子呢? 因为这样出栈的时候才是中左右的顺序。

func preorderTraversal(root *TreeNode) []int {
	ans := []int{}
	if root == nil {
		return ans
	}
	//用双端队列做栈
	st := list.New()
	st.PushBack(root)

	for st.Len() > 0 {
		node := st.Remove(st.Back()).(*TreeNode)
		ans = append(ans, node.Val)
		if node.Right != nil {
			st.PushBack(node.Right)
		}
		if node.Left != nil {
			st.PushBack(node.Left)
		}
	}
	return ans
}

3.2 二叉树的后序遍历

再来看后序遍历,先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了.
在这里插入图片描述

func postorderTraversal(root *TreeNode) []int {
    ans := []int{}

	if root == nil {
		return ans
	}

	st := list.New()
    st.PushBack(root)

    for st.Len() > 0 {
        node := st.Remove(st.Back()).(*TreeNode)

        ans = append(ans, node.Val)
        if node.Left != nil {
            st.PushBack(node.Left)
        }
        if node.Right != nil {
            st.PushBack(node.Right)
        }
    }
    reverse(ans)
    return ans
}

func reverse(a []int) {
    l, r := 0, len(a) - 1
    for l < r {
        a[l], a[r] = a[r], a[l]
        l, r = l+1, r-1
    }
}

3.3 二叉树的中序遍历

中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进result数组中),这就造成了处理顺序和访问顺序是不一致的。

那么在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。

func inorderTraversal(root *TreeNode) []int {
    ans := []int{}
    if root == nil {
        return ans
    }

    st := list.New()
    cur := root

    for cur != nil || st.Len() > 0 {
        if cur != nil {
            st.PushBack(cur)
            cur = cur.Left
        } else {
            cur = st.Remove(st.Back()).(*TreeNode)
            ans = append(ans, cur.Val)
            cur = cur.Right
        }
    }

    return ans
}

4. 二叉树的层序遍历

4.1 用递归的方式

func levelOrder(root *TreeNode) [][]int {
	arr := [][]int{}
	depth := 0
	order(root, depth,&arr)
	return arr
}

func order(root *TreeNode, depth int, arr *[][]int) {
	if root == nil {
		return
	}
    //代表还没有创建对应层的数组
	if len(*arr) == depth {
		*arr = append(*arr, []int{})
	}
	(*arr)[depth] = append((*arr)[depth], root.Val)
	order(root.Left, depth+1, arr)
	order(root.Right, depth+1, arr)
}

4.2 运用队列

func levelOrder(root *TreeNode) [][]int {
	res := [][]int{}
	if root == nil {
		return res
	}
	queue := list.New()
	queue.PushBack(root)

	var tmpArr []int

	for queue.Len() > 0 {
		length := queue.Len()
		for i := 0; i < length; i++ {
			node := queue.Remove(queue.Front()).(*TreeNode)
			if node.Left != nil {
				queue.PushBack(node.Left)
			}
			if node.Right != nil {
				queue.PushBack(node.Right)
			}
			tmpArr = append(tmpArr, node.Val)
		}
		res = append(res, tmpArr)
		tmpArr = []int{}
	}
	return res
}

4.3 其他题目

107 二叉树的层序遍历2

在这里插入图片描述
解题思路:正常层序遍历,最后翻转一下数组就可以

func levelOrderBottom(root *TreeNode) [][]int {
	res := [][]int{}
	if root == nil {
		return res
	}
	queue := list.New()
	queue.PushBack(root)

	var tmpArr []int

	for queue.Len() > 0 {
		length := queue.Len()
		for i := 0; i < length; i++ {
			node := queue.Remove(queue.Front()).(*TreeNode)
			if node.Left != nil {
				queue.PushBack(node.Left)
			}
			if node.Right != nil {
				queue.PushBack(node.Right)
			}
			tmpArr = append(tmpArr, node.Val)
		}
		res = append(res, tmpArr)
		tmpArr = []int{}
	}

    for i:=0;i<len(res)/2;i++{
        res[i],res[len(res)-1-i]=res[len(res)-1-i],res[i]
    }

	return res
}

199 二叉树的右视图

在这里插入图片描述
解题思路:正常层序遍历,最后取每个数组的最后一个元素

func rightSideView(root *TreeNode) []int {
	ans := []int{}
	
	if root == nil {
		return ans
	}

	layerOrder := [][]int{}
	queue := list.New()
	queue.PushBack(root)

	var tmpArr []int

	for queue.Len() > 0 {
		length := queue.Len()
		for i := 0; i < length; i++ {
			node := queue.Remove(queue.Front()).(*TreeNode)
			if node.Left != nil {
				queue.PushBack(node.Left)
			}
			if node.Right != nil {
				queue.PushBack(node.Right)
			}
			tmpArr = append(tmpArr, node.Val)
		}
		layerOrder = append(layerOrder, tmpArr)
		tmpArr = []int{}
	}

	for i:=0;i<len(layerOrder);i++{
		ans = append(ans,layerOrder[i][len(layerOrder[i])-1])
	}

	return ans
}

637 二叉树的层平均

在这里插入图片描述
思路:最后对每个层次进行平均。(其实可以在层序遍历的时候进行,加快速度)

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func averageOfLevels(root *TreeNode) []float64 {
	ans := []float64{}
	
	if root == nil {
		return ans
	}

	layerOrder := [][]int{}
	queue := list.New()
	queue.PushBack(root)

	var tmpArr []int

	for queue.Len() > 0 {
		length := queue.Len()
		for i := 0; i < length; i++ {
			node := queue.Remove(queue.Front()).(*TreeNode)
			if node.Left != nil {
				queue.PushBack(node.Left)
			}
			if node.Right != nil {
				queue.PushBack(node.Right)
			}
			tmpArr = append(tmpArr, node.Val)
		}
		layerOrder = append(layerOrder, tmpArr)
		tmpArr = []int{}
	}

	for i:=0;i<len(layerOrder);i++{
        ave,_ := calculateAverage(layerOrder[i])
		ans = append(ans,ave)
	}

	return ans
}

func calculateAverage(numbers []int) (average float64, ok bool) {
	if len(numbers) == 0 { // 检查切片是否为空
		return 0, false
	}

	sum := 0.0
	for _, number := range numbers {
		sum += float64(number)
	}

	average = sum / float64(len(numbers))
	return average, true
}

429 N叉树的层序遍历

在这里插入图片描述

func levelOrder(root *Node) [][]int {
	res := [][]int{} //结果集
	if root == nil {
		return res
	}
	queue := list.New()
	queue.PushBack(root)
	for queue.Len() > 0 {
		length := queue.Len()
		var tmp []int
		for T := 0; T < length; T++ { //该层的每个元素:一添加到该层的结果集中;二找到该元素的下层元素加入到队列中,方便下次使用
			myNode := queue.Remove(queue.Front()).(*Node)
			tmp = append(tmp, myNode.Val)
			for i := 0; i < len(myNode.Children); i++ {
				queue.PushBack(myNode.Children[i])
			}
		}
		res = append(res, tmp)
	}
	return res
}

515 在每个树行中找最大值

在这里插入图片描述

func largestValues(root *TreeNode) []int {
	ans := []int{}
	if root == nil {
		return ans
	}

	queue := list.New()
	queue.PushBack(root)

	for queue.Len() > 0 {
		length := queue.Len()
		maxVal := queue.Front().Value.(*TreeNode).Val
		for i := 0; i < length; i++ {
			node := queue.Remove(queue.Front()).(*TreeNode)
			if node.Left != nil {
				queue.PushBack(node.Left)
			}
			if node.Right != nil {
				queue.PushBack(node.Right)
			}
			if node.Val > maxVal {
				maxVal = node.Val
			}
		}
		ans = append(ans, maxVal)
	}
	return ans
}

116 填充每个节点的下一个右侧节点

层次遍历,将每一层串在一起即可。

func connect(root *Node) *Node {
	var res *Node
	if root == nil {
		return res
	}
	l := list.New()
	l.PushBack(root)
	num := l.Len()
	for num != 0 {
		firstNode := l.Front()
		if firstNode != nil {
			l.Remove(firstNode)
			
			n := firstNode.Value.(*Node)
			if n.Left != nil {
				l.PushBack(n.Left)
			}
			if n.Right != nil {
				l.PushBack(n.Right)
			}
		}
		for i := 1; i < num; i++ {
			secondNode := l.Front()
			if secondNode != nil {
				l.Remove(secondNode)
				n := secondNode.Value.(*Node)
				if n.Left != nil {
					l.PushBack(n.Left)
				}
				if n.Right != nil {
					l.PushBack(n.Right)
				}
			}
			if firstNode != nil && secondNode != nil {
				firstNode.Value.(*Node).Next = secondNode.Value.(*Node)
				firstNode = secondNode
			}
		}
		num = l.Len()
	}
	return root
}
func connect(root *Node) *Node {
	if root == nil {
        return root
    }
    queue := []*Node{root}
    for len(queue) > 0 {
        tmp := queue
        queue = nil
        for i,node := range tmp{
            if i+1<len(tmp){
                node.Next = tmp[i+1]
            }
            if node.Left != nil{
                queue = append(queue, node.Left)
            }
            if node.Right != nil{
                queue = append(queue, node.Right)
            }
        }
    }
    return root
}

104 二叉树的最大深度

可以使用递归法和迭代法,递归容易爆栈。迭代往往需要

//迭代法
func maxDepth(root *TreeNode) int {
	ans := 0
    if root == nil{
        return 0
    }
    queue := list.New()
    queue.PushBack(root)
    for queue.Len() > 0 {
        length := queue.Len()
        for i := 0; i < length; i++ {
            node := queue.Remove(queue.Front()).(*TreeNode)
            if node.Left != nil {
                queue.PushBack(node.Left)
            }
            if node.Right != nil {
                queue.PushBack(node.Right)
            }
        }
        ans++//记录深度,其他的是层序遍历的板子
    }
    return ans
}
//递归法
func maxDepth(root *TreeNode) int {
	return CalDepth(root, 0)
}

func CalDepth(root *TreeNode, height int) int {
	if root == nil {
		return height
	}
	return int(math.Max(float64(CalDepth(root.Left, height+1)), float64(CalDepth(root.Right, height+1))))
}

111 二叉树的最小深度

层次遍历查找第一个没有子节点的节点。

func minDepth(root *TreeNode) int {
	li := make([]*TreeNode, 0)
	tmp := make([]*TreeNode, 0)
	ans := 0
	if root == nil {
		return ans
	}
	li = append(li, root)
	for len(li) > 0 {
		length := len(li)
		ans++
		for i := 0; i < length; i++ {
			//查找第一个没有子节点的节点
			if li[i].Left == nil && li[i].Right == nil {
				return ans
			}
			if li[i].Left != nil {
				tmp = append(tmp, li[i].Left)
			}
			if li[i].Right != nil {
				tmp = append(tmp, li[i].Right)
			}
		}
		li = tmp
		tmp = nil
	}
	return ans
}

5. 翻转二叉树

在这里插入图片描述
翻转每个节点一次即可。

// 前序遍历
func invert(root *TreeNode) *TreeNode{
    if root==nil{
        return nil
    }
    root.Left,root.Right = root.Right,root.Left
    invert(root.Left)
    invert(root.Right)
    return root
}
// 后序遍历
func invert(root *TreeNode) *TreeNode{
    if root==nil{
        return nil
    }
    invert(root.Left)
    invert(root.Right)
    root.Left,root.Right = root.Right,root.Left
    return root
}
// 层次遍历
func invert(root *TreeNode) *TreeNode {
	l := list.New()
	if root == nil {
		return nil
	}
	l.PushBack(root)
	for l.Len() > 0 {
		n := l.Front()
		l.Remove(n)
		node := n.Value.(*TreeNode)
		node.Left,node.Right = node.Right,node.Left
		if node.Left != nil{
			l.PushBack(node.Left)
		}
		if node.Right != nil{
			l.PushBack(node.Right)
		}
	}
	return root
}
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值