力扣刷题 | 二叉树专题

本文详细解析了15道二叉树相关的算法题目,包括从上到下打印二叉树、计算深度、查找子结构、翻转、计算节点数、判断对称、平衡、子树、左下角节点、合并、搜索、验证、众数、最近公共祖先、插入、删除等操作。通过递归、双队列等方法进行高效实现,涉及二叉搜索树、深度优先搜索、广度优先搜索等概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

各自努力,最高处见!加油!

1、从上到下打印二叉树I

在这里插入图片描述

打印二叉树属于广度优先搜索(BFS)问题,通常用队列来解决问题。go语言中没有官方定义的队列类型,但是可以使用list包来完成队列的功能。
go语言中list的具体操作学习:https://haicoder.net/golang/golang-list.html

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func levelOrder(root *TreeNode) []int {
	result:=make([]int,0)

	if root==nil{
		return result
	}
	que:=list.New()
	que.PushBack(root)
	for que.Len()!=0{
		value := que.Front().Value.(*TreeNode)
		result=append(result,value.Val)
		if value.Left!=nil{
			que.PushBack(value.Left)
		}
		if value.Right!=nil{
			que.PushBack(value.Right)
		}
		que.Remove(que.Front())//更新队头
	}
	
	return result
}

2、 二叉树的深度

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func maxDepth(root *TreeNode) int {
    count:=0
    arr:=list.New()//存放一层数据
    que:=list.New()
    if root!=nil{
        arr.PushBack(root)
    }else{
        return count
    }
	

	for arr.Len()!=0{
        count++
        //把arr中存的一层数据全部放入队列中
        que.PushBackList(arr)
        arr=list.New()
        for que.Len()!=0{
            value := que.Front().Value.(*TreeNode)
            que.Remove(que.Front())//更新队头
            if value.Left!=nil{
                arr.PushBack(value.Left)
            }
            if value.Right!=nil{
                arr.PushBack(value.Right)
            }
        }

	}
    return count

}

上面方法是利用层序遍历的方法来计算深度,该方法利用了两个队列,消耗的内存很大,需要继续优化。

3、从上到下打印二叉树 II

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
import "container/list"
func levelOrder(root *TreeNode) [][]int {
    if root==nil{
        return nil
    }
    var array [][]int
    queue:=list.New()
    arr:=list.New()
    arr.PushBack(root)
    var value *TreeNode

    for arr.Len()!=0{
        temp:=make([]int,0)
        for arr.Len()!=0 {
            value = arr.Front().Value.(*TreeNode)
            queue.PushBack(value)
            temp=append(temp,value.Val)
            arr.Remove(arr.Front())//更新队头
        } 
        array=append(array,temp)

        for queue.Len()!=0{
            value=queue.Front().Value.(*TreeNode)
            if value.Left!=nil{
                arr.PushBack(value.Left)
            }
            if value.Right!=nil{
                arr.PushBack(value.Right)
            }
            queue.Remove(queue.Front())
        }
    }
    return array

}

双队列法实现,速度很快,但是消耗内存较多

4、从上到下打印二叉树 III

在这里插入图片描述在这里插入图片描述

多了一步数组的倒序处理,这个方法的运行速度很快,但是内存占比很大,如何优化使用内存?

func levelOrder(root *TreeNode) [][]int {

    queue:=list.New()
    next:=list.New()
    statu:=true
    result:=make([][]int,0)
    if root==nil {
            return nil
    }
    queue.PushBack(root)
    for queue.Len()!=0{
        array:=make([]int,0)
        for queue.Len()!=0{
            var value *TreeNode
            value=queue.Front().Value.(*TreeNode)//取出队头
            queue.Remove(queue.Front())//更新队头
            array=append(array,value.Val)//加入数组中

            if value.Left!=nil{
                next.PushBack(value.Left)
            }
            if value.Right!=nil{
                next.PushBack(value.Right)
            }
        }
        if statu == false {
			l := len(array)
			for i := 0; i < l-1-i; i++ {
				temp := array[l-1-i]
				array[l-1-i] = array[i]
				array[i] = temp
			}
		}
        result=append(result,array)//数组输出
        queue.PushBackList(next)
        next=list.New()
        //转换输出方式
        if statu==true{
            statu=false
        }else{
            statu=true
        }
    }
    return result

}

5、对称的二叉树

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func isSymmetric(root *TreeNode) bool {
    if root==nil{
        return true
    }
    return recure(root.Left,root.Right)
}
func recure(L,R *TreeNode)bool{
    if L==nil&&R==nil{
        return true
    }
    if L==nil||R==nil||L.Val!=R.Val{
        return false
    }

    f1:=recure(L.Left,R.Right)
    f2:=recure(L.Right,R.Left)

    return f1&&f2
}

递归法
在这里插入图片描述

6、平衡二叉树

在这里插入图片描述
在这里插入图片描述

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func isBalanced(root *TreeNode) bool {
    if recure(root)==-1{
        return false
    }
    return true
}

func recure(root *TreeNode) int {
    if nil==root{
        return 0
    }
    L:=recure(root.Left)
    if L==-1{
        return -1
    }

    R:=recure(root.Right)
    if R==-1{
        return -1
    }

    if L-R<2&&L-R>-2{
        return max(L,R)+1
    }

    return -1
}

func max(a int,b int) int{
    if a>b{
        return a
    }
    return b
}

解题的关键点:如何求解二叉树的深度差

此树的深度 =max(左子树的深度 , 右子树的深度) +1

递归的方法,从底层开始算起,根节点为nil就向上返回0,表示这是第0层。此后每层的计算方法都按照上面的公式计算,逐层递增1,直到根节点。在递归过程中,如果出现一边的返回结果为-1,说明有子树不满足平衡二叉树的要求,直接返回-1.

7、树的子结构

在这里插入图片描述
在这里插入图片描述

回溯法+递归比对
用递归函数查找某节点的树结构跟目标树结构是否一致。
回溯法切换查找的节点。

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func isSubStructure(A *TreeNode, B *TreeNode) bool {
    if B==nil||A==nil{
        return false
    }

    return (recur(A,B)||isSubStructure(A.Left,B)||isSubStructure(A.Right,B))
    // return recur(A,B)//这种写法只是判断根节点开始的树有没有与目标树相同的结构

}

func recur(A *TreeNode, B *TreeNode) bool{//递归结构:如果节点的值相同,判断它的左右子树是否跟目标结构相同
    if B==nil{
        return true//子树的节点为空,说明检索完成,存在完全相同的子结构
    }
    if A==nil||A.Val!=B.Val{
        return false
    }
    return recur(A.Left,B.Left)&&recur(A.Right,B.Right)
}

8、翻转二叉树

在这里插入图片描述

递归解法:递归前序遍历翻转每个节点的左右节点

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func invertTree(root *TreeNode) *TreeNode {
    if root==nil{
        return nil
    }
    //交换左右节点
    temp:=root.Left
    root.Left=root.Right
    root.Right=temp

    invertTree(root.Left)
    invertTree(root.Right)

    return root
}

前序迭代法

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func invertTree(root *TreeNode) *TreeNode {
    if root==nil{
        return nil
    }
    stack:=list.New()
    stack.PushBack(root)
    // stack.PushBack(nil)

    for stack.Len()>0{
        back:=stack.Back().Value
        stack.Remove(stack.Back())
        if back==nil{
            node:=stack.Back().Value.(*TreeNode)
            stack.Remove(stack.Back())
            node.Left,node.Right=node.Right,node.Left
            continue
        }
        node:=back.(*TreeNode)
        if node.Right!=nil{
            stack.PushBack(node.Right)
        }
        if node.Left!=nil{
            stack.PushBack(node.Left)
        }
        stack.PushBack(node)
        stack.PushBack(nil)
    }
    return root

}

9、完全二叉树的节点个数

在这里插入图片描述

层序遍历解法

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func countNodes(root *TreeNode) int {
    //层序遍历
    count:=0
    if root==nil{
        return count
    }
    queue:=list.New()
    queue.PushBack(root)
    count++
    for queue.Len()>0{
        front:=queue.Front().Value.(*TreeNode)
        queue.Remove(queue.Front())
        if front.Left!=nil{
            count++
            queue.PushBack(front.Left)
        }
        if front.Right!=nil{
            count++
            queue.PushBack(front.Right)
        }

    }
    return count

}

完全二叉树递归解法

10、另一棵树的子树

在这里插入图片描述

递归解法
暴力遍历该二叉树的节点,如果发现跟subroot根节点值相同的节点,就以该节点为根节点判断该子树是否与subroot树结构相同。isSub()判断两棵树是否为相同的树。

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func isSubtree(root *TreeNode, subRoot *TreeNode) bool {
    if root==nil&&subRoot==nil{
        return true
    }
    if root==nil||subRoot==nil{
        return false
    }
    if root.Val==subRoot.Val{
        if isSub(root,subRoot){
            return true
        }
    }
    return isSubtree(root.Left,subRoot)||isSubtree(root.Right,subRoot)
}
func isSub(root *TreeNode, subRoot *TreeNode)bool{//
    if root==nil&&subRoot==nil{
        return true
    }
    if root==nil||subRoot==nil||root.Val!=subRoot.Val{
        return false
    }
    return isSub(root.Left,subRoot.Left)&&isSub(root.Right,subRoot.Right)
}

11、找树左下角的值

在这里插入图片描述
递归法
需要搞明白的两个问题:
1、最左边的节点不一定在最底层
2、最底层最左边的节点不一定是左节点
递归解答本题的核心在于

if root.Left==nil&&root.Right==nil{//叶子节点
        if length>maxLength{//只有该层的第一个元素执行这个if语句
            maxLength=length
            leftNode=root.Val
        }
}

扫描到叶子节点就检测该节点所在的层是否大于已经扫描的最大层数,如果大于,就更新全局数据。这个if语句只有在扫描到每一层的第一个节点才会更新,所以就保证了该数据存的是该层的最左节点。

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
var maxLength int
var leftNode int
func findBottomLeftValue(root *TreeNode) int {
    //递归法需要搞明白的两个问题:
    //1、最左边的节点不一定在最底层
    //2、最底层最左边的节点不一定是左节点
    if root.Left==nil&&root.Right==nil{
        return root.Val
    }
    maxLength=0
    leftNode=0
    findLeftLeaves(root,0)
    return leftNode

}
func findLeftLeaves(root *TreeNode,length int){//当前为length层
    if root.Left==nil&&root.Right==nil{//叶子节点
        if length>maxLength{//只有该层的第一个元素执行这个if语句
            maxLength=length
            leftNode=root.Val
        }
    }

    if root.Left!=nil{
        findLeftLeaves(root.Left,length+1)
    }
    if root.Right!=nil{
        findLeftLeaves(root.Right,length+1)
    }
}

12、合并二叉树

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {
    if root1==nil&&root2==nil{
        return nil
    }else if root1==nil{
        return root2
    }else if root2==nil{
        return root1
    }
    // root:=&TreeNode{
    //     Val:root2.Val+root1.Val,
    //     Left:mergeTrees(root1.Left,root2.Left),
    //     Right:mergeTrees(root1.Right,root2.Right),
    // }
    root1.Val+=root2.Val
    root1.Left=mergeTrees(root1.Left,root2.Left)
    root1.Right=mergeTrees(root1.Right,root2.Right)
    //递归是从最底层回溯,所以可以更改root1节点
    
    return root1
}

13、二叉搜索树中的搜索

在这里插入图片描述

循环解法

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func searchBST(root *TreeNode, val int) *TreeNode {
    
    for {
        if root==nil{
            return nil
        }else if root.Val==val{
            return root
        }
        if root.Val>val{
            root=root.Left
        }else{
            root=root.Right
        }
        
    }
}

递归解法

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func searchBST(root *TreeNode, val int) *TreeNode {
    if root==nil{
        return nil
    }
    if root.Val==val{
        return root
    }
    if root.Val<val{
        return searchBST(root.Right,val)
    }else{
        return searchBST(root.Left,val)
    }
}

14、验证二叉搜索树

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
var isBST func(node *TreeNode) bool
func isValidBST(root *TreeNode) bool {
	var pre *TreeNode = nil

	isBST = func(root *TreeNode) bool {
		if root == nil {
			return true
		}
		left := isBST(root.Left)

		if pre != nil && pre.Val >= root.Val {
			return false
		}
		pre = root

		right := isBST(root.Right)

		return left && right
	}

	return isBST(root)

}

15、二叉搜索树中的众数

在这里插入图片描述
递归解法:
判断截止该节点,相同值的节点数量是否大于maxCount,如果大于则更新maxCount并清空数组的数据。

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
var searchBST func(*TreeNode)
func findMode(root *TreeNode) []int {
    res:=make([]int,0)
    if root.Left==nil&&root.Right==nil{//only one
        res=append(res,root.Val)
        return res
    }
    var pre *TreeNode
    maxCount:=0
    count:=0
    searchBST=func(root *TreeNode){
        if root==nil{
            return 
        }
        searchBST(root.Left)
        if pre==nil{
            count=1
        }else if pre!=nil{
            if pre.Val==root.Val{
                count++
            }else{
                
                count=1//重新计数
            }
            
        }
        pre=root
        if count>maxCount{//计算到本节点为止众数的数量是否超过maxCount
            res=make([]int,0)
            res=append(res,pre.Val)
            maxCount=count
        }else if count==maxCount{
            res=append(res,pre.Val)
        }
        searchBST(root.Right)
    }
    searchBST(root)
    return res


}

16、二叉树的最近公共祖先

在这里插入图片描述如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。(摘自代码随想录)这道题有一个点是,即使两个pq两个节点都在根节点的左子树,其右子树一样也会遍历,只不过返回的是nil。如在本题例子中(上图),p为jiedian4,q为节点5,公共祖先为5.采用如下递归法遍历到5时便向上一层返回该节点。而1节点返回nil,这时程序就可以判定5就是公共祖先。

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
     if root==nil||root==p||root==q{
         return root
     }


     left:=lowestCommonAncestor(root.Left,p,q)
     right:=lowestCommonAncestor(root.Right,p,q)
     if left!=nil&&right!=nil{
         return root
     }else if left==nil&&right!=nil{
        return right
     }else if left!=nil&&right==nil{
         return left
     }else{
         return nil
     }
}

17、二叉搜索树中的插入操作

在这里插入图片描述

递归解法
我写的代码如下,我的思路是只有当val所在的方向为空时,才接受返回值,但是其实这一步完全可以省略。因为这是在递归中,在递归回溯的时候,左子节点或右子节点会赋值到原来的位置。见改进写法(参考代码随想录)。

func insertIntoBST(root *TreeNode, val int) *TreeNode {
    if root==nil{
        root=&TreeNode{
            Val:val,
            Left:nil,
            Right:nil,
        }
    }
    if val<root.Val&&root.Left!=nil{
        insertIntoBST(root.Left,val)
    }else if val>root.Val&&root.Right!=nil{
        insertIntoBST(root.Right,val)
    }else if val<root.Val&&root.Left==nil{
        root.Left=insertIntoBST(root.Left,val)
    }else if val>root.Val&&root.Right==nil{
        root.Right=insertIntoBST(root.Right,val)
    }
    return root
}

改进写法

func insertIntoBST(root *TreeNode, val int) *TreeNode {
    if root==nil{
        root=&TreeNode{
            Val:val,
            Left:nil,
            Right:nil,
        }
    }
    if val<root.Val{
        root.Left=insertIntoBST(root.Left,val)
    }else if val>root.Val{
        root.Right=insertIntoBST(root.Right,val)
    }
    return root
}

18、删除二叉搜索树中的节点

在这里插入图片描述

递归法分情况讨论:代码随想录yyds

  • 第一种情况:没找到删除的节点,遍历到空节点直接返回了

  • 找到删除的节点

  1. 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点

  2. 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点

  3. 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点

  4. 第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。

func deleteNode(root *TreeNode, key int) *TreeNode {
    if root==nil{
        return nil
    }
    if root.Val==key{//找到删除的节点
        if root.Left==nil&&root.Right==nil{
            return nil
        }
        if root.Left==nil&&root.Right!=nil{
            return root.Right
        }
        if root.Right==nil&&root.Left!=nil{
            return root.Left
        }
        if root.Right!=nil&&root.Left!=nil{
            ptr:=root.Right
            for ptr.Left!=nil{
                ptr=ptr.Left
            }
            ptr.Left=root.Left
            return root.Right
        }
    }
    //没找到要删除的节点
    root.Left=deleteNode(root.Left,key)
    root.Right=deleteNode(root.Right,key)
    return root
    
}

19、将有序数组转换为二叉搜索树(递归+二分思想)

在这里插入图片描述

func sortedArrayToBST(nums []int) *TreeNode {
    return traversal(nums,0,len(nums)-1)
}
func traversal(nums []int,left int,right int)*TreeNode{
    if left>right{
        return nil
    }
    mid:=left+(right-left)/2
    root:=&TreeNode{
        Val:nums[mid],
        Left:traversal(nums,left,mid-1),
        Right:traversal(nums,mid+1,right),
    }
    return root
}

经验:

力扣runtime: out of memory: cannot allocate 4194304-byte block (469499904 in use

出现这种错误先检查代码中是否存在死循环,可能是循环的条件有错或者for循环的 i 变量没有递增导致的。

如何计算树的最大深度

func maxdepth(root *TreeNode)int{
    if root==nil{
        return 0
    }
    return max(maxdepth(root.Left),maxdepth(root.Right))+1
}
func max(a,b int)int{
    if a>b{
        return a
    }
    return b
}

递归法什么时候有返回值?

如果需要遍历整棵树,递归函数就不能有返回值。如果需要遍历某一条固定路线,递归函数就一定要有返回值!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值