算法练习-第十五天【二叉树】

二叉树

226. 翻转二叉树

参考:代码随想录

思路

翻转二叉树的方式:

  1. 递归
  2. 迭代法
  3. 层序遍历

1.递归

前序遍历

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func invertTree(root *TreeNode) *TreeNode {
	if root == nil {
		return root
	}
	// 递归前序遍历
	root.Left, root.Right = root.Right, root.Left
	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 root
	}
	// 递归后序遍历
	invertTree(root.Left)
	invertTree(root.Right)
	root.Left, root.Right = root.Right, root.Left

	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 root
	}
	// 递归中序遍历
	invertTree(root.Left)
	root.Left, root.Right = root.Right, root.Left
	invertTree(root.Left)

	return root
}

2.迭代法

以下代码使用前序遍历的方式,后序遍历也类似。

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func invertTree(root *TreeNode) *TreeNode {
	// 前序遍历
	if root == nil {
		return root
	}
	stack := list.New()
	stack.PushBack(root)
	for stack.Len() > 0 {
		e := stack.Back()
		if e.Value != nil {
			cur := e.Value.(*TreeNode)
			stack.Remove(e)
			if cur.Right != nil {
				stack.PushBack(cur.Right)
			}
			if cur.Left != nil {
				stack.PushBack(cur.Left)
			}
			stack.PushBack(cur)
			stack.PushBack(nil)
		} else {
			stack.Remove(e)
			cur := stack.Remove(stack.Back()).(*TreeNode)
			cur.Left, cur.Right = cur.Right, cur.Left
		}
	}

	return root
}

3.层序遍历

使用层序遍历,翻转每一层的各个节点

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func invertTree(root *TreeNode) *TreeNode {
	if root == nil {
		return root
	}
	queue := list.New()
	queue.PushBack(root)
	for queue.Len() > 0 {
		length := queue.Len()
		// 处理每一层的各个节点
		for ; length > 0; length-- {
			cur := queue.Remove(queue.Front()).(*TreeNode)
			cur.Left, cur.Right = cur.Right, cur.Left // 交换
			if cur.Left != nil {
				queue.PushBack(cur.Left)
			}
			if cur.Right != nil {
				queue.PushBack(cur.Right)
			}
		}
	}

	return root
}

总结

翻转二叉树可以有多种实现方式,递归的方式容易理解和实现。但是不建议使用中序遍历容易造成错误。
迭代的方法中可以模拟深度优先搜索和广度优先搜索。

101. 对称二叉树

参考:代码随想录

思路

判断二叉树是否对称,需要将树的左子树的左节点与右子树的右节点对比,左子树的右节点与右子树的左节点进行对比。
使用递归

  1. 确定递归函数参数与返回值func dfs(left *TreeNode, right *TreeNode) bool
  2. 确定递归终止条件
    • 左右节点都为空,则true
    • 左右有一个不为空,则false
    • 左右都不为空,但数值不相等
  3. 单层递归逻辑
    • 比较左子树的左节点与右子树的右节点
    • 比较左子树的右节点与右子树的左节点
    • 左右都为true, 返回true, 否则false

递归

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func isSymmetric(root *TreeNode) bool {
	return dfs(root.Left, root.Right)
}

func dfs(left *TreeNode, right *TreeNode) bool {
	// 左子树为空 右子树为空
	if left == nil && right == nil {
		return true
	}
	if left == nil || right == nil {
		return false
	}
	if left.Val != right.Val {
		return false
	}

	outer := dfs(left.Left, right.Right) // 左子树: 左	右子树: 右
	inner := dfs(left.Right, right.Left) // 左子树: 右	右子树: 左

	return outer && inner // 左子树: 中 	右子树: 中
}

迭代法

使用队列判断左右子树是否相等

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func isSymmetric(root *TreeNode) bool {
	if root == nil {
		return false
	}
	queue := list.New()
	queue.PushBack(root.Left)
	queue.PushBack(root.Right)
	for queue.Len() > 0 {
		//从队列中取出
		leftNode := queue.Remove(queue.Front()).(*TreeNode)
		rightNode := queue.Remove(queue.Front()).(*TreeNode)
		if leftNode == nil && rightNode == nil { // 此时对称
			continue
		}
		if leftNode == nil || rightNode == nil || (leftNode.Val != rightNode.Val) {
			return false
		}
		queue.PushBack(leftNode.Left)   // 加入左节点左孩子
		queue.PushBack(rightNode.Right) // 加入右节点右孩子
		queue.PushBack(rightNode.Left)  // 加入右节点左孩子
		queue.PushBack(leftNode.Right)  // 加入左节点右孩子
	}
	return true
}

总结

以上代码不是最简单的形式,但是思路比较清晰易于理解。迭代法就是将左右子树依次放到队列中,然后依次取出进行比对。按照此思路使用栈也是可以处理的。

posted @ 2022-10-11 01:23  neil_liu  阅读(34)  评论(0)    收藏  举报