代码随想录算法训练营Day16|二叉树part04

80 阅读6分钟

LeetCode 513 找树左下角的值

题目链接:leetcode.cn/problems/fi…

文档讲解:programmercarl.com/0513.找树左下角的…

视频讲解:www.bilibili.com/video/BV142…

思路

左下角的值:树最后一行最左边的值 涉及到最后一行,使用层序遍历会简单很多。

迭代法:层序遍历

全局变量mostLeft保存层序遍历中每一层最左边的值。因为最后遍历的一定是最后一层,所以此变量最后留下的一定是最后一层最左边的值

递归法

从深度优先搜索角度考虑,树左下角的值是前序遍历到的第一个深度最大的值。 因此在前序遍历过程中,用全局变量记录递归的深度,当每次首次到一个更大的深度,就记录下节点的值到全局变量mostLeft。这样最后留下的值一定是全局最大深度下第一个值,即左下角的值

考虑递归三要素:

  1. 递归函数的参数和返回值 全局变量:mostLeft记录最左值,depth记录深度,maxDepth记录曾经到达过的最大深度 参数:根节点 返回值:空
  2. 递归函数的结束条件 节点为空,返回
  3. 单层递归逻辑 depth+1 如果depth>maxDepth,把当前节点值记录在mostLeft中,并更新最大深度 分别对左右子节点递归调用 depth-1

解法

迭代法:层序遍历

class Solution {
	public int findBottomLeftValue(TreeNode root) {	
		Queue<TreeNode> queue = new LinkedList<>();		
		int mostLeft = 0;		
		queue.offer(root);		
		while (!queue.isEmpty()) {		
			int size = queue.size();			
			for (int i = 0; i < size; i++) {			
				TreeNode node = queue.poll();				
				if (i==0) {				
					mostLeft = node.val;				
				}				
				if (node.left != null) {				
					queue.offer(node.left);				
				}				
				if (node.right != null) {				
					queue.offer(node.right);				
				}			
			}		
		}		
		return mostLeft;	
	}
}

递归法

class Solution {
	int depth;	
	int maxDepth;	
	int mostLeft;	
	public int findBottomLeftValue(TreeNode root) {	
		depth = 0;		
		maxDepth = 0;		
		mostLeft = 0;		
		traverse(root);		
		return mostLeft;	
	}		  
	
	public void traverse(TreeNode root) {	
		if (root == null) {		
			return ;		
		}		
		depth++;		
		if (maxDepth < depth) {		
			maxDepth = depth;			
			mostLeft = root.val;		
		}		
		traverse(root.left);		
		traverse(root.right);		
		depth--;	
	}
}

LeetCode 112&113 路径总和

题目链接:leetcode.cn/problems/pa…

文档讲解:programmercarl.com/0112.路径总和.h…

视频讲解:www.bilibili.com/video/BV19t…

112思路

考虑递归三要素:

  1. 递归函数的参数和返回值 全局变量:当前路径总和sumPath,标志位flag 参数:根节点root 返回值:空
  2. 递归函数的结束条件 根节点为空,返回
  3. 单层递归逻辑 在路径中加入本节点的值 如果本节点为叶子节点,判断路径总和是否满足要求,是则把flag置为true 如果不是叶子节点,对左右子节点递归调用 在路径中减去本节点的值

112解法

个人认为,回溯相比于单纯的递归需要多考虑:当函数在返回时,变量的状态。通常的操作就是要把状态变量还原

class Solution {
	boolean flag = false;	
	int pathSum = 0;	
	int targetSum;	
	public boolean hasPathSum(TreeNode root, int targetSum) {	
		this.targetSum = targetSum;		
		traverse(root);		
		return flag;	
	}	
	  	
	public void traverse(TreeNode root) {	
		if (root == null) {		
			return ;		
		}		
		pathSum += root.val;		
		if (root.left == null && root.right == null) {		
			if (pathSum == this.targetSum) {			
				flag = true;			
			}		
		}		
		else {		
			traverse(root.left);			
			traverse(root.right);			
		}		
		pathSum -= root.val;	
	}
}

113思路

考虑递归三要素:

  1. 递归函数的参数和返回值 全局变量:path当前路径,pathSum当前路径和,result结果集 参数:根节点 返回值:空
  2. 递归函数的结束条件 当根节点为空,返回
  3. 单层递归逻辑 更新path和pathSum 如果节点是叶子节点,判断路径和是否满足要求,是则把路径的副本加入结果集合 不是叶子节点则分别对左右子节点递归调用 还原path和pathSum

113解法

class Solution {
	int targetSum;	
	int pathSum;	
	List<Integer> path;	
	List<List<Integer>> result;
	
	public List<List<Integer>> pathSum(TreeNode root, int targetSum) {	
		pathSum = 0;		
		this.targetSum = targetSum;		
		result = new ArrayList<>();		
		path = new ArrayList<>();		
		traverse(root);		
		return result;	
	}		  
	
	public void traverse(TreeNode root) {	
		if (root == null) {		
			return ;		
		}		
		path.add(root.val);		
		pathSum += root.val;		
		if (root.left == null && root.right == null) {		
			if (pathSum == this.targetSum) {			
				result.add(new ArrayList<>(path));			
			}		
		}		
		else {		
			traverse(root.left);			
			traverse(root.right);		
		}		
		path.remove(path.size()-1);		
		pathSum -= root.val;	
	}
}

LeetCode 106 从中序与后序遍历序列构造二叉树

题目链接:leetcode.cn/problems/co…

文档讲解:programmercarl.com/0106.从中序与后序…

视频讲解:www.bilibili.com/video/BV1vW…

思路

考虑树的中序遍历:左子树-根节点-右子树

考虑树的后序遍历:左子树-右子树-根节点

所以根据后序遍历的最后一个节点,可以在中序遍历中分割出左右子树。左子树在中序后序遍历中位置相同,所以在相同位置取子数组就可以得到左子树的中序和后序遍历,对应也得到右子树的。再次应用根节点在末尾原则,又得到了左右子树的根节点。如此递归下去,就可以构建出二叉树。

考虑递归三要素:

  1. 递归函数的参数和返回值 参数:中序数组inOrder,后序数组postOrder 返回值:构建出的树的根节点root
  2. 递归函数的结束条件 数组为空,返回null
  3. 单层递归逻辑 取后序数组的最后一个元素构建根节点 用根节点分割子数组 递归对左右子树调用,返回值赋给根节点的左右指针 返回根节点

解法

class Solution {
	public TreeNode buildTree(int[] inorder, int[] postorder) {	
		if (inorder.length == 0) {		
			return null;		
		}		
		TreeNode root = new TreeNode(postorder[postorder.length-1]);			
		int rootIndex = 0;		
		for (int i = 0; i < inorder.length; i++) {		
			if (inorder[i] == root.val) {			
				rootIndex = i;				
				break;			
			}		
		}		
		int[] inOrderLeft = Arrays.copyOfRange(inorder, 0, rootIndex);		
		int[] postorderLeft = Arrays.copyOfRange(postorder, 0, rootIndex);		
		int[] inOrderRight = Arrays.copyOfRange(inorder, rootIndex+1, inorder.length);		
		int[] postOrderRight = Arrays.copyOfRange(postorder, rootIndex, postorder.length-1);		
		root.left = buildTree(inOrderLeft, postorderLeft);		
		root.right = buildTree(inOrderRight, postOrderRight);		
		return root;	
	}
}

LeetCode 105 从前序与中序遍历序列构造二叉树

题目链接:leetcode.cn/problems/co…

文档讲解:programmercarl.com/0106.从中序与后序…

思路

考虑树的中序遍历:左子树-根节点-右子树

考虑树的前序遍历:根节点-左子树-右子树

所以根据前序遍历的第一个节点,可以在中序遍历中找到根节点索引并分割出左右子树。右子树在中序前序遍历中位置相同,所以在相同位置取子数组就可以得到右子树的中序和后序遍历,对应也得到左子树的。再次应用根节点在首部原则,又得到了左右子树的根节点。如此递归下去,就可以构建出二叉树。

考虑递归三要素:

  1. 递归函数的参数和返回值 参数:中序数组inOrder,前序数组preOrder 返回值:构建出的树的根节点root
  2. 递归函数的结束条件 数组为空,返回null
  3. 单层递归逻辑 取前序数组的第一个元素构建根节点 用根节点分割子数组 递归对左右子树调用,返回值赋给根节点的左右指针 返回根节点

解法

class Solution {
	public TreeNode buildTree(int[] preorder, int[] inorder) {	
		if (preorder.length == 0) {		
			return null;		
		}		
		TreeNode root = new TreeNode(preorder[0]);		
		int rootIndex = -1;		
		for (int i = 0; i < inorder.length; i++) {		
			if (inorder[i] == root.val) {			
				rootIndex = i;				
				break;			
			}		
		}		
		int[] inorderLeft = Arrays.copyOfRange(inorder, 0, rootIndex);		
		int[] inorderRight = Arrays.copyOfRange(inorder, rootIndex+1, inorder.length);		
		int[] preorderRight = Arrays.copyOfRange(preorder, rootIndex+1, preorder.length);		
		int[] preorderLeft = Arrays.copyOfRange(preorder, 1, rootIndex+1);		
		root.left = buildTree(preorderLeft, inorderLeft);		
		root.right = buildTree(preorderRight, inorderRight);		
		return root;
	}
}

今日收获总结

今日学习4小时,除了最后两题构造二叉树都独立做出来了。构造二叉树的思路还需要复习~