题目传送门
题解
func inorderTraversal(root *TreeNode) []int {
var res []int
if root == nil {
return res
}
var dfs func(root *TreeNode, res *[]int)
dfs = func(root *TreeNode, res *[]int) {
if root.Left != nil {
dfs(root.Left, res)
}
*res = append(*res, root.Val)
if root.Right != nil {
dfs(root.Right, res)
}
}
dfs(root, &res)
return res
}
题目传送门
题解
使用队列实现:
func levelOrder(root *TreeNode) [][]int {
var res [][]int
if root == nil {
return res
}
// 创建一个q队列,每次将一层的节点都入队
// 第一次将根节点入队
q := []*TreeNode{root}
// 不停的入队一层的节点
for i := 0; len(q) > 0; i++ {
// 新开一层
res = append(res, []int{})
// p队列用来将当前层所有节点的孩子节点入队,即入队下一层的元素
var p []*TreeNode
// 遍历当前层节点
for j := 0; j < len(q); j++ {
// 将当前层的所有节点添加到res数组中
node := q[j]
res[i] = append(res[i], node.Val)
// 入队下一层节点
if node.Left != nil {
p = append(p, node.Left)
}
if node.Right != nil {
p = append(p, node.Right)
}
}
// 遍历下一层
q = p
}
return res
}
递归实现:
func levelOrder(root *TreeNode) [][]int {
var res [][]int
var dfs func(node *TreeNode, depth int)
dfs = func(node *TreeNode, depth int) {
// 递归出口
if node == nil {
return
}
// 初始化新的一层的储存空间
if len(res) == depth {
res = append(res, []int{})
}
// 将第depth层的节点添加到res中
res[depth] = append(res[depth], node.Val)
if node.Left != nil {
dfs(node.Left, depth + 1)
}
if node.Right != nil {
dfs(node.Right, depth + 1)
}
}
dfs(root, 0)
return res
}
题目传送门
func buildTree(preorder []int, inorder []int) *TreeNode {
if len(preorder) < 1 || len(inorder) < 1 {
return nil
}
// 寻找中序数组中的根节点下标
root := findRoot(preorder[0], inorder)
// 递归构建二叉树
treeNode := &TreeNode{
Val: preorder[0],
// 左子树就是前序数组的[1:root+1], 中序数组的[0:root]
Left: buildTree(preorder[1:root+1], inorder[0:root]),
// 右子树就是前序数组的[root+1:], 中序数组的[root+1:]
Right: buildTree(preorder[root+1:], inorder[root+1:]),
}
return treeNode
}
// 根据根节点的值寻找根节点在中序数组中的下标
func findRoot(value int, arr []int) int {
for i, v := range arr {
if v == value {
return i
}
}
return -1
}
题目传送门
func buildTree(inorder []int, postorder []int) *TreeNode {
if len(inorder) < 1 || len(postorder) < 1 {
return nil
}
// 根节点就是后续数组的最后一位
root := findRoot(inorder, postorder[len(postorder) - 1])
return &TreeNode{
Val: postorder[len(postorder) - 1],
// 左子树就是中序数组的inorder[:root], 后续数组的postorder[:root]
Left: buildTree(inorder[:root], postorder[:root]),
// 右子树就是中序数组的inorder[root+1:],后续数组的postorder[root:len(postorder) - 1]
Right: buildTree(inorder[root+1:], postorder[root:len(postorder) - 1]),
}
}
func findRoot(arr []int, target int) int {
for i, v := range arr {
if v == target {
return i
}
}
return -1
}
题目传送门
题解
求二叉树的深度加上判断左右节点是否平衡即可。
func isBalanced(root *TreeNode) bool {
if root == nil {
return true
}
return abs(dfs(root.Left) - dfs(root.Right)) <= 1 && isBalanced(root.Left) && isBalanced(root.Right)
}
func dfs(t *TreeNode) int {
if t == nil {
return 0
}
lChild := dfs(t.Left)
rChild := dfs(t.Right)
return 1 + max(lChild, rChild)
}
func max(a, b int) int {
if a > b {
return a
} else {
return b
}
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
题目传送门
题解
方法一:使用前序遍历的思想
首先创建一个TreeNode的指针数组res,然后使用前序遍历的顺序递归遍历二叉树,然后将遍历到的每一个节点存到res数组中,最后通过res数组来构建题目要求的二叉树即可。
func flatten(root *TreeNode) {
res := Preorder(root)
for i := 1; i < len(res); i++ {
pre, cur := res[i - 1], res[i]
pre.Left, pre.Right = nil, cur
}
}
func Preorder(root *TreeNode) (res []*TreeNode) {
if root != nil {
res = append(res, root)
res = append(res, Preorder(root.Left)...)
res = append(res, Preorder(root.Right)...)
}
return
}
方法二:不使用额外的空间
首先判断当前节点有无左子树,如果有,就找到左子树中的最后一个节点,按照前序遍历的顺序,左子树中的最后一个节点就是右子树的前驱节点。这个时候我们将右子树直接移到这个前驱节点的后面,接着将整个左子树移到右子树的位置,再将左子树的位置置为nil,最后继续往右子树遍历。
func flatten1(root *TreeNode) {
cur := root
for cur != nil {
// 首先判断当前节点的左子树是否为空
if cur.Left != nil {
next := cur.Left
// 寻找左子树中的最后一个节点
// 按照前序遍历的规律,这个节点就是右子树的前驱节点
pre := cur.Left
for pre.Right != nil {
pre = pre.Right
}
pre.Right = cur.Right
cur.Left, cur.Right = nil, next
}
cur = cur.Right
}
}
题目传送门
题解
首先我们定义一个概念,一个节点的最大贡献值:指的是以这个节点为根节点,包含的到叶子节点的最大值.
所以root节点的最大路径和其实就是root节点的值 + 左子树的最大贡献值 + 右子树的最大贡献值.
func maxPathSum(root *TreeNode) int {
maxSum := math.MinInt32
var maxPrice func(node *TreeNode) int
maxPrice = func(node *TreeNode) int {
if node == nil {
return 0
}
// 计算左子树的最大贡献值
// 与0作比较是为了防止累加到负数
leftPrice := max(maxPrice(node.Left), 0)
// 计算右子树的最大贡献值
rightPrice := max(maxPrice(node.Right), 0)
// 加上当前节点的路径和
currPrice := node.Val + leftPrice + rightPrice
// 更新最大路径和
maxSum = max(maxSum, currPrice)
// 返回当前节点的最大贡献值
return node.Val + max(leftPrice, rightPrice)
}
// 从根节点开始递归
maxPrice(root)
return maxSum
}
func max(a, b int) int {
if a > b {
return a
} else {
return b
}
}
题目传送门
题解
func preorderTraversal(root *TreeNode) []int {
var res []int
if root == nil {
return res
}
var dfs func(root *TreeNode, res *[]int)
dfs = func(root *TreeNode, res *[]int) {
*res = append(*res, root.Val)
if root.Left != nil {
dfs(root.Left, res)
}
if root.Right != nil {
dfs(root.Right, res)
}
}
dfs(root, &res)
return res
}
题目传送门
题解
func postorderTraversal(root *TreeNode) []int {
var res []int
if root == nil {
return res
}
var dfs func(root *TreeNode, res *[]int)
dfs = func(root *TreeNode, res *[]int) {
if root.Left != nil {
dfs(root.Left, res)
}
if root.Right != nil {
dfs(root.Right, res)
}
*res = append(*res, root.Val)
}
dfs(root, &res)
return res
}
题目传送门
题解
使用前序遍历来通过二叉树构建字符串.
type Codec struct {
}
func Constructor() (_ Codec) {
return
}
// Serializes a tree to a single string.
func (this *Codec) serialize(root *TreeNode) string {
sb := &strings.Builder{}
var dfs func(node *TreeNode)
dfs = func(node *TreeNode) {
if node == nil {
sb.WriteString("null,")
return
}
sb.WriteString(strconv.Itoa(node.Val))
sb.WriteString(",")
dfs(node.Left)
dfs(node.Right)
}
dfs(root)
return sb.String()
}
// Deserializes your encoded data to tree.
func (this *Codec) deserialize(data string) *TreeNode {
sp := strings.Split(data, ",")
var build func() *TreeNode
build = func() *TreeNode {
if sp[0] == "null" {
sp = sp[1:]
return nil
}
val, _ := strconv.Atoi(sp[0])
sp = sp[1:]
return &TreeNode{val, build(), build()}
}
return build()
}
题目传送门
题解
二叉树的直径其实就是左子树的最大深度+右子树的最大深度+根节点.
func diameterOfBinaryTree(root *TreeNode) int {
maxLength := 0
var measure func(root *TreeNode) int
measure = func(root *TreeNode) int {
if root == nil {
return 0
} else {
lHeight := measure(root.Left)
rHeight := measure(root.Right)
maxLength = max(maxLength, lHeight+rHeight)
return 1 + max(lHeight, rHeight)
}
}
measure(root)
return maxLength
}
func max(a, b int) int {
if a > b {
return a
} else {
return b
}
}
题目传送门
题解
直接使用DFS同时遍历两棵树即可.
func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {
var dfs func(r1, r2 *TreeNode) *TreeNode
dfs = func(r1, r2 *TreeNode) *TreeNode {
if r1 == nil {
return r2
}
if r2 == nil {
return r1
}
r1.Val = r1.Val + r2.Val
r1.Left = dfs(r1.Left, r2.Left)
r1.Right = dfs(r1.Right, r2.Right)
return r1
}
return dfs(root1, root2)
}
题目传送门
题解
使用dfs求解即可。
func isSubPath(head *ListNode, root *TreeNode) bool {
var dfs func(head *ListNode, root *TreeNode) bool
dfs = func(head *ListNode, root *TreeNode) bool {
// 链表已经全部匹配完全,成功
if head == nil {
return true
}
// 二叉树访问到了空节点,失败
if root == nil {
return false
}
// 当前二叉树的节点值与链表的节点值不相等
if head.Val != root.Val {
return false
}
// 继续往后递归
return dfs(head.Next, root.Left) || dfs(head.Next, root.Right)
}
if root == nil {
return false
}
return dfs(head, root) || isSubPath(head, root.Left) || isSubPath(head, root.Right)
}
题目传送门
题解
实际上是根据前序数组和中序数组来重建二叉树。
func buildTree(preorder []int, inorder []int) *TreeNode {
if len(preorder) < 1 || len(inorder) < 1 {
return nil
}
// 寻找根节点在中序数组中的下标
root := findRootIndex(preorder[0], inorder)
// 递归构造二叉树节点
treeNode := &TreeNode{
Val: preorder[0],
Left: buildTree(preorder[1:root+1], inorder[:root]),
Right: buildTree(preorder[root+1:], inorder[root+1:]),
}
return treeNode
}
func findRootIndex(target int, arr []int) int {
for i := 0; i < len(arr); i++ {
if arr[i] == target {
return i
}
}
return -1
}
题目传送门
题解
首先分情况考虑:
定义一个函数isContain
用来判断在根节点重合的情况下,A是否包含B。
func isSubStructure(A *TreeNode, B *TreeNode) bool {
// AB任何一个节点为空都不成立
if A == nil || B == nil {
return false
}
// 如果AB的根节点重复,直接判断A是否包含B
if A.Val == B.Val && isContain(A, B) {
return true
} else {
// 如果AB根节点不重复,判断A的子树是否包含B
return isSubStructure(A.Left, B) || isSubStructure(A.Right, B)
}
}
// 该函数是个递归函数,用来判断根节点重合的情况下B是否被A包含
func isContain(A, B *TreeNode) bool {
// B遍历到了空,B必被A包含
if B == nil {
return true
}
// B不为空,A为空,B不被A包含
if A == nil {
return false
}
// AB两个节点的值一样,进一步比较他们的左右子树是否相等
if A.Val == B.Val {
return isContain(A.Left, B.Left) && isContain(A.Right, B.Right)
} else {
// AB两个节点的值不同,B必不被A包含
return false
}
}
题目传送门
题解
这道题其实就是翻转二叉树,首先我们需要使用先序遍历遍历二叉树,然后从叶子节点开始翻转二叉树,如果遍历到了空节点就直接返回nil。
func mirrorTree(root *TreeNode) *TreeNode {
// 遍历到空节点,不用翻转,直接返回nil
if root == nil {
return nil
} else {
// 获取左子树翻转后的结果
left := mirrorTree(root.Left)
// 获取右子树翻转后的结果
right := mirrorTree(root.Right)
// 翻转左右子树
root.Left, root.Right = right, left
}
return root
}
题目传送门
题解
一个二叉树对称,它和它的镜像完全相同。创建一个递归函数,来比较二叉树和它的镜像是否相等。
步骤:
func isSymmetric(root *TreeNode) bool {
var check func(r1, r2 *TreeNode) bool
check = func(r1, r2 *TreeNode) bool {
// 两个节点都遍历到了空,说明相等
if r1 == nil && r2 == nil {
return true
}
// 一个节点为空另一个节点不为空,必然不相等
if r1 == nil || r2 == nil {
return false
}
// 两个节点值相等,然后做镜像对比
return r1.Val == r2.Val && check(r1.Left, r2.Right) && check(r1.Right, r2.Left)
}
return check(root, root)
}
题目传送门
题解
这道题其实就是二叉树的层次遍历,应该使用BFS来求解。
步骤:
q := make([]*TreeNode, 0)
,是一个用于bfs遍历的队列。func levelOrder(root *TreeNode) (res []int) {
if root == nil {
return
}
// 定义用于bfs的队列
q := make([]*TreeNode, 0)
q = append(q, root)
for i := 0; i < len(q); i++ {
node := q[i]
res = append(res, node.Val)
if node.Left != nil {
q = append(q, node.Left)
}
if node.Right != nil {
q = append(q, node.Right)
}
}
return
}
题目传送门
题解
这道题使用BFS,关键点是创建两个临时切片q和p,都是*TreeNode
类型,q用于遍历当前层的节点,p用于备份当前层节点的子节点,一层遍历结束之后,就将q = p,开始遍历下一层。
func levelOrder1(root *TreeNode) (res [][]int) {
if root == nil {
return
}
q := []*TreeNode{root}
for i := 0; len(q) > 0; i++ {
// 创建新的一层
res = append(res, []int{})
var p []*TreeNode
for j := 0; j < len(q); j++ {
// 将当前节点值加入res数组中
res[i] = append(res[i], q[j].Val)
// 备份当前节点的孩子节点
if q[j].Left != nil {
p = append(p, q[j].Left)
}
if q[j].Right != nil {
p = append(p, q[j].Right)
}
}
q = p
}
return
}
题目传送门
题解
这道题的解法与上一道是差不多的,不同的是这道题当遍历到奇数层的时候,应该把res数组对应的层翻转一下。
func levelOrder(root *TreeNode) (res [][]int) {
if root == nil {
return
}
q := []*TreeNode{root}
for i := 0; len(q) > 0; i++ {
// 创建新的一层
res = append(res, []int{})
// 创建用于备份当前层孩子节点的p切片
var p []*TreeNode
for j := 0; j < len(q); j++ {
// 将当前节点值加入到res数组中
res[i] = append(res[i], q[j].Val)
// 开始备份当前节点的子节点
if q[j].Left != nil {
p = append(p, q[j].Left)
}
if q[j].Right != nil {
p = append(p, q[j].Right)
}
}
q = p
// 如果是奇数层,需要翻转那一层的数组
if i%2 != 0 {
reverse(res[i])
}
}
return
}
func reverse(arr []int) {
for i, n := 0, len(arr); i < n/2; i++ {
arr[i], arr[n-i-1] = arr[n-i-1], arr[i]
}
}
题目传送门
题解
二叉树后序遍历的序列,最后一个元素是元素的根节点,然后数组的左边部分是左子树,右边部分是右子树,其中,左子树部分的元素都比根节点小,右子树部分的元素都比根节点大。
步骤:
func verifyPostorder(postorder []int) bool {
// 如果序列中的元素数量小于2,那么肯定是后续遍历的序列
if len(postorder) < 2 {
return true
}
// index用于区分左右子树(下标)
index := len(postorder) - 1
// 用于记录根节点的值
rootValue := postorder[index]
for k, v := range postorder {
// 当出现第一个大于根节点的值时,这个值往后全是右子树的值
if index == len(postorder)-1 && v > rootValue {
index = k
}
// 如果右子树中出现了小于根节点的值,就返回fasle
if index != len(postorder)-1 && v < rootValue {
return false
}
}
// 继续递归左右子树
return verifyPostorder(postorder[:index]) && verifyPostorder(postorder[index:len(postorder)-1])
}
题目传送门
题解
我们从根节点开始,枚举每一条从根节点到叶子节点的路径,中间用一个临时切片path保存某一条路径上的节点值。
步骤:
func pathSum(root *TreeNode, target int) (res [][]int) {
var path []int
var dfs func(node *TreeNode, left int)
dfs = func(node *TreeNode, left int) {
if node == nil {
return
}
left -= node.Val
path = append(path, node.Val)
// 回溯
defer func() {
path = path[:len(path)-1]
}()
// 判断是否到达叶子节点,且累加和刚好等于target
if node.Left == nil && node.Right == nil && left == 0 {
// path切片是用于累加的中间变量,构造结果的时候要创建一个新的切片
res = append(res, append([]int(nil), path...))
return
}
dfs(node.Left, left)
dfs(node.Right, left)
}
dfs(root, target)
return
}
题目传送门
题解
这道题要求寻找第k大的元素,也就是从最大的元素开始递减查找,因为二叉树中的较大元素都在右子树,所以我们使用中序遍历+优先遍历右子树的方式来进行遍历。
func kthLargest(root *TreeNode, k int) (res int) {
var dfs func(node *TreeNode)
dfs = func(node *TreeNode) {
if node == nil {
return
}
// 先遍历右子树
dfs(node.Right)
k--
if k == 0 {
res = node.Val
return
}
dfs(node.Left)
}
dfs(root)
return
}
题目传送门
题解
递归从叶子节点开始统计,在递归的每一层,左子树高就返回左子树高度+1,右子树高就返回右子树高度+1。
func maxDepth(root *TreeNode) int {
var dfs func(node *TreeNode) int
dfs = func(node *TreeNode) int {
if node == nil {
return 0
} else {
lson := dfs(node.Left)
rson := dfs(node.Right)
return max(lson, rson) + 1
}
}
return dfs(root)
}
func max(a, b int) int {
if a > b {
return a
} else {
return b
}
}
题目传送门
题解
首先定义一个求二叉树高度的函数。具体做法类似于二叉树的前序遍历,即对于当前遍历到的节点,首先计算左右子树的高度,如果左右子树的高度差是否不超过 1,再分别递归地遍历左右子节点,并判断左子树和右子树是否平衡。这是一个自顶向下的递归的过程。
func isBalanced(root *TreeNode) bool {
if root == nil {
return true
}
// 计算左右子树的高度是否符合要求
// 然后再递归判断左右子树的高度是否平衡
return abs(dfs(root.Left)-dfs(root.Right)) <= 1 && isBalanced(root.Left) && isBalanced(root.Right)
}
// 求二叉树的高度
func dfs(node *TreeNode) int {
if node == nil {
return 0
} else {
lson := dfs(node.Left)
rson := dfs(node.Right)
return max(lson, rson) + 1
}
}
func max(a, b int) int {
if a > b {
return a
} else {
return b
}
}
func abs(n int) int {
if n < 0 {
return -n
} else {
return n
}
}
题目传送门
题解
存在三种情况:
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
if root == nil {
return nil
}
// root为p或q中的一个
if root == p || root == q {
return root
}
// 递归遍历左子树和右子树
left := lowestCommonAncestor(root.Left, p, q)
right := lowestCommonAncestor(root.Right, p, q)
// 如果分别在左右子树中,公共祖先就是root
if left != nil && right != nil {
return root
// 都分布在左子树中
} else if left != nil {
return left
// 都分布在右子树中
} else {
return right
}
}
代码中如果p,q全都分布在左子树或者右子树上就直接返回left或right,这个时候能不能保证是最近公共祖先呢?其实是可以的,因为递归遍历以后,最终return是从叶子节点开始return的。