代码随想录算法训练营Day15

59 阅读6分钟

代码随想录算法训练营Day15

二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

示例 1:

img

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

示例 2:

输入:root = [1]
输出:[[1]]

示例 3:

输入:root = []
输出:[]

提示:

  • 树中节点数目在范围 [0, 2000]
  • -1000 <= Node.val <= 1000

思路

二叉树的层序遍历类似于图的广度优先遍历,推广到n叉树的层序遍历也是可以的。需要用到队列来进行算法实现:

初始条件

  • 新建空数组作为返回值result
  • 判定输入的根节点是否为空,若为空,直接返回result
  • 新建一个队列queue,类型为二叉树节点(或者指针)。将根节点入队,进入循环中

循环结束条件

如果queue为空,则循环结束。

循环内部逻辑

  • 取得当前队列的长度len,表示的是本层的所有节点个数,根据len对这一层的所有节点进行处理
  • 将出队节点current的数据val加入result中
  • 如果current的左孩子不为空,则将其加入queue中
  • 如果current的右孩子不为空,则将其加入queue中

代码实现

func levelOrder(root *TreeNode) [][]int {
	result := [][]int{}
	queue := []*TreeNode{}
	if root != nil {
		queue = append(queue, root)
	}
	for len(queue) > 0 {
		line := []int{}
		currentLen := len(queue) //取得当前层次的节点数
		for i := 0; i < currentLen; i++ {
			//根据节点数出队,记录本层的节点信息
			head := queue[0]
			queue = queue[1:]
			//将当前出队节点数据加入这一行的数组中
			line = append(line, head.Val)
			if head.Left != nil {
				queue = append(queue, head.Left)
			}
			if head.Right != nil {
				queue = append(queue, head.Right)
			}
		}
		if len(line) > 0 {
			result = append(result, line)
		}
	}
	return result
}

image-20240118021919098

226.翻转二叉树

题目

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

示例 1:

img

输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

示例 2:

img

输入:root = [2,1,3]
输出:[2,3,1]

示例 3:

输入:root = []
输出:[]

提示:

  • 树中节点数目范围在 [0, 100]
  • -100 <= Node.val <= 100

思路

利用二叉树的遍历即可,将访问节点的处理换成交换左右子树即可。

代码实现

(迭代形式的前序遍历模板)

func invertTree(root *TreeNode) *TreeNode {
	// 根为空直接返回
	if root == nil{
		return nil
	}
	stack := []*TreeNode{root}
	for len(stack) > 0 {
		//取栈顶元素
		node := stack[len(stack) - 1]
		stack = stack[:len(stack) - 1];
		//交换左右子树
		node.Right, node.Left = node.Left,node.Right
		//继续前序遍历
		if node.Right != nil {
			stack = append(stack, node.Right)
		}
		if node.Left != nil {
			stack = append(stack, node.Left)
		}
	}
	return root
}

image-20240118023731050

101. 对称二叉树

题目

给你一个二叉树的根节点 root , 检查它是否轴对称。

示例 1:

img

输入:root = [1,2,2,3,4,4,3]
输出:true

示例 2:

img

输入:root = [1,2,2,null,3,null,3]
输出:false

提示:

  • 树中节点数目在范围 [1, 1000]
  • -100 <= Node.val <= 100

思路

递归实现

确定递归函数的参数和返回值

每一轮要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。

判定是或者不是,返回值用bool型即可。

确定终止条件

理想情况下,输入的两个子树节点在整个树中的位置都是对称的,根据left和right是否为nil可以分类为:

  1. left为空,righ不为空:显然不对称,返回false
  2. left不为空,righ为空:显然不对称,返回false
  3. left、righ都为空:显然对称,返回true
  4. left、righ都不为空:
    • left.val和right.val不相等:显然不对称,返回false
    • left.val和right.val相等:当前节点对称,最终结果要根据下一层的判定结果得出
确定单层递归的逻辑

上面提到,理想情况下,输入的两个子树节点在整个树中的位置都是对称的,我们可以从最简单的情况开始推演递归的流程

image-20240118170352856

第一轮对比的是根节点的两个孩子节点,假设当前判定两个节点的值都相同,则需要继续判定才能得出结论。那么接下来应该应该比较那些节点呢,显然接下来需要检查的还是对称位置的两个节点,不难发现,接下来的需要比较的应该比较的是left的左孩子和right的右孩子(外侧节点),以及left的右孩子和right的左孩子(内侧节点)

image-20240118171759372

image-20240118171820258

推广到任意两个正在比较的节点(分别记为left和right),由于left和right对称,则left的左孩子和right的右孩子、left的右孩子和right的左孩子的位置都分别成对对称。如果left和right这两个节点都非空且值相同,那么就需要继续递归确定上述的两对子节点才可以得出结论。只有两对子节点的比较结果都是对称,才表示left和right分出去的子树都是对称的。

迭代实现

从递归方式的流程可以看出,每轮对比的其实就是两个对称节点,如果当前对比的节点对称,则需要准备对比下一轮,这和层序遍历很类似,因此可以用队列来实现。

迭代方式

每轮还是对比两个节点,如果提前遇到判定为不对称的情况,则可以直接返回false;但如果是遇到两者为空,迭代方法不能直接返回true,因为和递归方式不同,迭代时两者为空是局部结果且不能重新利用。

如果当前节点非空且值相等,那么就需要继续比较,此时就需要把需要继续比较的节点放入到队列中。

迭代结束条件

提前判定为不对称的情况和递归方法相同;判定为对称则需要让所有对比都顺利匹配成功,此时队列会为空,可以返回true。

代码实现

递归方法

// 递归比较方法
func compare(left, right *TreeNode) bool {
	if left == nil && right != nil { //left为空,righ不为空:显然不对称,返回false
		return false
	} else if left != nil && right == nil { //left不为空,righ为空:显然不对称,返回false
		return false
	} else if left == nil && right == nil { //left、righ都为空:显然对称,返回true
		return true
	} else if left.Val != right.Val { //left.val和right.val不相等:显然不对称,返回false
		return false
	}
	//left.val和right.val相等:当前节点对称,但最终结果要根据下一层的判定结果得出
	outside := compare(left.Left, right.Right)
	inside := compare(left.Right, right.Left)
	return outside && inside
}

func isSymmetric(root *TreeNode) bool {
	if root == nil {
		return true
	}
	return compare(root.Left, root.Right)
}

image-20240118172637088

迭代方法

func isSymmetric(root *TreeNode) bool {
	if root == nil {
		return true
	}
	queue := []*TreeNode{root.Left, root.Right}
	for len(queue) > 0 {
		//每次出队两个节点进行对比
		left := queue[0]				
		right := queue[1]
		queue = queue[2:]
		//都为空可以提前结束此轮迭代
		if left == nil && right == nil {
			continue
		}
		//提前判定不对称的情况和递归相同
		if left == nil || right == nil || left.Val != right.Val {
			return false
		}
		//如果对比节点非空且值相同,则将需要比较的组合依次加入队列中
		queue = append(queue, left.Left, right.Right, left.Right, right.Left)
	}
	//如果能顺利匹配所有节点,说明是对称的
	return true
}

image-20240118174835046