Warning
本文最后更新于 July 31, 2022,文中内容可能已过时,请谨慎使用。
二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。以下会以几道 LeetCode 巩固自己的基础
leetcode 树
生成一个二叉树
二叉树结构
1
2
3
4
5
| type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
|
二叉树的工厂模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 创建节点
func CreateNode(val int) (node *TreeNode) {
return &TreeNode{val,nil,nil}
}
// 打印节点
func (node *TreeNode) Print() {
fmt.Print(node.Val)
}
// 设置节点
func (node *TreeNode) SetVal(val int) {
node.Val = val
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| func (node *TreeNode) Add(val int) {
if val < node.Val {
if node.Left == nil {
node.Left = CreateNode(val)
}else {
node.Left.Add(val)
}
}else {
if node.Right == nil {
node.Right = CreateNode(val)
}else {
node.Right.Add(val)
}
}
}
|
二叉树的遍历
经典的方法有三种,前序遍历、中序遍历和后序遍历。这三种遍历都是 DFS 深度优先遍历算法
其中,前、中、后序,表示的是节点与它的左右子树节点遍历打印的先后顺序。动画演示
前序遍历是指,对于树中的任意节点来说,先打印这个节点,然后再打印它的左子树,最后打印它的右子树。
中序遍历是指,对于树中的任意节点来说,先打印它的左子树,然后再打印它本身,最后打印它的右子树。
后序遍历是指,对于树中的任意节点来说,先打印它的左子树,然后再打印它的右子树,最后打印这个节点本身。
1
2
3
4
5
6
7
8
9
|
前序遍历的递推公式:
preOrder(r) = print r->preOrder(r->left)->preOrder(r->right)
中序遍历的递推公式:(中序排列结果为有序数组)
inOrder(r) = inOrder(r->left)->print r->inOrder(r->right)
后序遍历的递推公式:
postOrder(r) = postOrder(r->left)->postOrder(r->right)->print r
|
LC 二叉树 前序遍历 (DFS)
例题链接:https://leetcode-cn.com/leetbook/read/data-structure-binary-tree/xeywh5/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| func preorderTraversal(root *TreeNode) []int {
if root == nil {
return []int{}
}
var list []int
list = append(list, root.Val)
resLeft := preorderTraversal(root.Left)
resRight := preorderTraversal(root.Right)
return append(list, append(resLeft, resRight...)...)
}
func main() {
treeRoot := CreateNode(0)
treeRoot.Add(-2)
treeRoot.Add(3)
treeRoot.Add(8)
treeRoot.Add(4)
treeRoot.Add(5)
treeRoot.Add(-2)
treeRoot.Add(1)
treeRoot.Add(2)
fmt.Println(preorderTraversal(treeRoot))
}
// 打印
[0 -2 -2 3 1 2 8 4 5]
|
LC 二叉树 中序遍历(DFS)
1
2
3
4
5
6
7
8
9
10
11
12
13
| func inorderTraversal(root *TreeNode) []int {
if root == nil {
return []int{}
}
var list []int
list = append(list, root.Val)
resLeft := inorderTraversal(root.Left)
resRight := inorderTraversal(root.Right)
return append(resLeft,append(list,...), resRight...)
}
// 打印
[-2 -2 0 1 2 3 4 5 8]
|
LC 二叉树 后序遍历(DFS)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| func postorderTraversal(root *TreeNode) []int {
if root == nil {
return []int{}
}
var list []int
list = append(list, root.Val)
resLeft := postorderTraversal(root.Left)
resRight := postorderTraversal(root.Right)
return append(resLeft, append(resRight,list...)...)
}
// 打印
[0 -2 -2 3 1 2 8 4 5]
|
二叉树的层次遍历(BFS广度优先遍历)
例题链接:https://leetcode-cn.com/leetbook/read/data-structure-binary-tree/xefh1i/
题解:
创建一个先进先出的队列,从最顶层的节点依次加入节点,并遍历该层,每遍历该层的一个节点,把该层的子节点加入的队列的后面,这样就可以实现层次遍历,如果要蛇形遍历也是一个道理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| func levelOrder(root *TreeNode) [][]int {
var level [][]int
if root == nil {
return level
}
queue := list.New() // 一个先进先出的队列,所有的元素需要依次进队
queue.PushFront(root)
for queue.Len() > 0 {
var curLevel []int
curLevelLength := queue.Len()
for i := 0; i < curLevelLength; i++ {
// 从尾部抽出上一层,新一层追加到队前
node := queue.Remove(queue.Back()).(*TreeNode)
curLevel = append(curLevel, node.Val)
if node.Right != nil { // 下一层遍历做准备, 把当前节点的子节点都加进去
queue.PushFront(node.Right)
}
if node.Left != nil {
queue.PushFront(node.Left)
}
}
level = append(level, curLevel)
}
return level
}
|
LeetCode
No.100 相同的树
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| func isSameTree(p *TreeNode, q *TreeNode) bool {
if p == nil && q == nil {
return true
}
if p == nil {
return false
}
if q == nil {
return false
}
return q.Val == p.Val && isSameTree(p.Left, q.Left) && isSameTree(p.Right, q.Right)
}
func isSameTree(p *TreeNode, q *TreeNode) bool {
if (p == nil && q != nil) || (p != nil && q == nil) {
return false
}
if p == nil && q == nil {
return true
}
return p.Val == q.Val && isSameTree(p.Left, q.Left) && isSameTree(p.Right, q.Right)
}
|
No.101 对称二叉树
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| func isSameTree(p *TreeNode, q *TreeNode) bool {
if p == nil || q == nil {
return p == q
}
if q.Val == p.Val {
return isSameTree(p.Left,q.Right) && isSameTree(p.Right,q.Left)
}else {
return false
}
}
func isSymmetric(root *TreeNode) bool {
if root == nil {
return true
}
return isSameTree(root.Left, root.Right)
}
|
No.104 二叉树的最大深度
1
2
3
4
5
6
7
| func maxDepth(root *TreeNode) int {
if root == nil {
return 0
}
return int(math.Max(float64(maxDepth(root.Left)),float64(maxDepth(root.Right))) + 1)
}
|
No.108 将有序数组转为二叉搜索树
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| func sortedArrayToBST(nums []int) *TreeNode {
return helper(nums, 0, len(nums) - 1)
}
func helper(nums []int, left, right int) *TreeNode {
if left > right {
return nil
}
mid := (left + right) / 2
leftNode = helper(nums, left, mid - 1)
rightNode = helper(nums, mid + 1, right)
return &TreeNode{Val: nums[mid], Left: leftNode, Right: rightNode}
}
|
No.110 判断平衡二叉树
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // 获取树的高度
func getTreeDepth(root *TreeNode) int {
if root == nil {
return 0
}
return int(math.Max(float64(getTreeDepth(root.Left)), float64(getTreeDepth(root.Right)))) + 1
}
func isBalanced(root *TreeNode) bool {
if root == nil {
return true
}
left := getTreeDepth(root.Left)
right := getTreeDepth(root.Right)
// 判断左子树和右子树的绝对差值小于1 且 左子树 和 右子树 也满足平衡
if math.Abs(float64(left) - float64(right)) <= 1 && isBalanced(root.Left) && isBalanced(root.Right) {
return true
}
return false
}
|
No.111 二叉树的最小深度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| func min(a,b int) int {
if a < b {
return a
}
return b
}
func minDepth(root *TreeNode) int {
if root == nil {
return 0
}
if root.Right == nil {
return 1 + minDepth(root.Left)
}
if root.Left == nil {
return 1 + minDepth(root.Right)
}
return min(minDepth(root.Left), minDepth(root.Right))+1
}
|
No.112 路径总和
1
2
3
4
5
6
7
8
9
10
| func hasPathSum(root *TreeNode, sum int) bool {
if root == nil {
return false
}
if root.Left == nil && root.Right == nil {
return sum == root.Val
}
return hasPathSum(root.Left, sum-root.Val) || hasPathSum(root.Right, sum-root.Val)
}
|
No.226 翻转二叉树
链接:https://leetcode.cn/problems/invert-binary-tree/
思路:老样子,肯定是一道递归题,我们只需要思考当前一层该如何计算即可,假设
1
2 3
这样一颗树,那么翻转就是 left := root.Left
right := root.Right
最后再 root.Left = right root.Right = left
即可。很显然,左子数将重复进行上述递归操作,同理右子树。
1
2
3
4
5
6
7
8
9
10
11
12
| func invertTree(root *TreeNode) *TreeNode {
if root == nil {
return nil
}
left := invertTree(root.Left)
right := invertTree(root.Right)
root.Left = right
root.Right = left
return root
}
|
No.235 二叉搜索树的公共祖先
1
2
3
4
5
6
7
8
9
10
11
12
13
| func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
if root == nil || root == q || root == p {
return root
}
if p.Val <= root.Val && q.Val <= root.Val { //p和q都小于root,公共祖先节点一定在root的左边
return lowestCommonAncestor(root.Left,p,q)
}
if p.Val >= root.Val && q.Val >= root.Val { //p和q都大于root,公共祖先节点一定在root的右边
return lowestCommonAncestor(root.Right,p,q)
}
return root
}
|
No.404 左叶子之和
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| // 如果左子树上的节点全部计算,则是下面的解法,但不是本题想要的结果
func sumOfLeftLeaves(root *TreeNode) int {
if root == nil {
return 0
}
var left int
if root.Left != nil {
left = root.Left.Val
}
return left + sumOfLeftLeaves(root.Left) + sumOfLeftLeaves(root.Right)
}
// 为了符合本题要求,改造一下
func isTreeNode(root *TreeNode) bool {
return root.Right == nil && root.Left == nil
}
func sumOfLeftLeaves(root *TreeNode) int {
if root == nil {
return 0
}
var left int
if root.Left != nil && isTreeNode(root.Left) {
left = root.Left.Val
}
return left + sumOfLeftLeaves(root.Left) + sumOfLeftLeaves(root.Right)
}
|
不过看了题解,比较麻烦,竟然直接上 dfs 了,这里贴一下官方的解法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| func isLeafNode(node *TreeNode) bool {
return node.Left == nil && node.Right == nil
}
func dfs(node *TreeNode) (ans int) {
if node.Left != nil {
if isLeafNode(node.Left) {
ans += node.Left.Val
} else {
ans += dfs(node.Left)
}
}
if node.Right != nil && !isLeafNode(node.Right) {
ans += dfs(node.Right)
}
return
}
func sumOfLeftLeaves(root *TreeNode) int {
if root == nil {
return 0
}
return dfs(root)
}
|
No.563 二叉树的坡度
一个树的 节点的坡度 定义即为,该节点左子树的节点之和和右子树节点之和的 差的绝对值 。如果没有左子树的话,左子树的节点之和为 0 ;没有右子树的话也是一样。空结点的坡度是 0 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| func abs(a,b int) int {
if a-b > 0 {
return a-b
} else {
return b-a
}
}
func findTilt(root *TreeNode) (ans int) {
var dfs func(*TreeNode) int
dfs = func(node *TreeNode) int {
if node == nil {
return 0
}
sumLeft := dfs(node.Left)
sumRight := dfs(node.Right)
ans += abs(sumLeft,sumRight)
return sumLeft + sumRight + node.Val
}
dfs(root)
return
}
|
No.637 二叉树的层平均值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| func averageOfLevels(root *TreeNode) []float64 {
var res []float64
if root == nil {
return res
}
queue := list.New()
queue.PushFront(root)
for queue.Len() > 0 {
queueLength := queue.Len()
levelSum := 0
for i := 0 ; i < queueLength ; i ++ {
node := queue.Remove(queue.Front()).(*TreeNode)
if node.Left != nil {
queue.PushBack(node.Left)
}
if node.Right != nil {
queue.PushBack(node.Right)
}
levelSum += node.Val
}
if queueLength != 0 {
res = append(res, float64(levelSum/queueLength))
}
}
return res
}
|
No.662 二叉树的最大宽度
记root节点位置为1。可以发现左节点位置是父节点位置x2 - 1,右节点位置是父节点位置x 2,最后将每层最右边的位置-最左边的位置+1即是该层的层宽
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| func widthOfBinaryTree(root *TreeNode) int {
if root == nil {
return 0
}
queue := list.New()
queue.PushFront(root)
var maxWidth int // 记录最大宽度
NodeTreeLocation := make(map[*TreeNode]int) // 记录每一个节点的位置信息
for queue.Len() != 0 {
length := queue.Len()
curLevelMin := NodeTreeLocation[queue.Front().Value.(*TreeNode)] // 当前层最左节点位置(默认第一个)
curLevelMax := NodeTreeLocation[queue.Front().Value.(*TreeNode)] // 当前层最右节点位置(默认第一个)
for i := 0; i < length ; i ++ {
cur := queue.Remove(queue.Back()).(*TreeNode)
node := NodeTreeLocation[cur] // 父节点的位置
if node > curLevelMax { // 寻找当前层的最大节点
curLevelMax = node
}
if node < curLevelMin {
curLevelMin = node
}
if cur.Left != nil { // 对下一层节点位置进行赋值
queue.PushFront(cur.Left)
NodeTreeLocation[cur.Left] = node * 2 - 1
}
if cur.Right != nil {
queue.PushFront(cur.Right)
NodeTreeLocation[cur.Right] = node * 2
}
}
if maxWidth < (curLevelMax - curLevelMin) + 1 {
maxWidth = (curLevelMax - curLevelMin) + 1
}
}
return maxWidth
}
|