二叉树与分治法算法

Posted 每日算法之刻意练习

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉树与分治法算法相关的知识,希望对你有一定的参考价值。

二叉树的前序遍历 · Binary Tree Preorder Traversal

二叉树的前序遍历题解:非递归方式实现前序遍历时,首先存入当前节点值,然后先将右儿子压入栈中,再将左儿子压入栈中。对栈中元素遍历访问。



给出一棵二叉树,返回其节点值的前序遍历。
首个数据为根节点,后面接着是其左儿子和右儿子节点值,"#"表示不存在该子节点。节点数量不超过20样例样例 1
输入:
二叉树 = {1,2,3}输出:
[1,2,3]解释:
1 / \2 3它将被序列化为{1,2,3}前序遍历
样例 2
输入:
二叉树 = {1,#,2,3}输出:
[1,2,3]解释:
1 \ 2 /3它将被序列化为{1,#,2,3}前序遍历
/** * Definition of TreeNode: * public class TreeNode { * public int val; * public TreeNode left, right; * public TreeNode(int val) { * this.val = val; * this.left = this.right = null; * } * } */
public class Solution { /** * @param root: A Tree * @return: Preorder in ArrayList which contains node values. */ List<Integer> list = new ArrayList<Integer>(); public List<Integer> preorderTraversal(TreeNode root) { // write your code here if(root == null) { ; } else { list.add(root.val); preorderTraversal(root.left); preorderTraversal(root.right); }
return list;
}}
Version 0: Non-Recursion (Recommend)/** * Definition for binary tree * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */public class Solution { public List<Integer> preorderTraversal(TreeNode root) { Stack<TreeNode> stack = new Stack<TreeNode>(); List<Integer> preorder = new ArrayList<Integer>();  if (root == null) { return preorder; }  stack.push(root); while (!stack.empty()) { TreeNode node = stack.pop(); preorder.add(node.val); if (node.right != null) { stack.push(node.right); } if (node.left != null) { stack.push(node.left); } }  return preorder; }}
//Version 1: Traversepublic class Solution { public ArrayList<Integer> preorderTraversal(TreeNode root) { ArrayList<Integer> result = new ArrayList<Integer>(); traverse(root, result); return result; } // 把root为跟的preorder加入result里面 private void traverse(TreeNode root, ArrayList<Integer> result) { if (root == null) { return; }
result.add(root.val); traverse(root.left, result); traverse(root.right, result); }}
//Version 2: Divide & Conquerpublic class Solution { public ArrayList<Integer> preorderTraversal(TreeNode root) { ArrayList<Integer> result = new ArrayList<Integer>(); // null or leaf if (root == null) { return result; }
// Divide ArrayList<Integer> left = preorderTraversal(root.left); ArrayList<Integer> right = preorderTraversal(root.right);
// Conquer result.add(root.val); result.addAll(left); result.addAll(right); return result; }}

• Traverse vs Divide Conquer 

    • They are both Recursion Algorithm 

    • Result in parameter vs Result in return value

    • Top down vs Bottom up


97 · Maximum Depth of Binary Tree

描述给定一个二叉树,找出其最大深度。
最大深度是从根节点到叶节点的最长路径的节点数。
最终答案不会超过 5000
样例样例 1:
输入: tree = {}输出: 0 样例解释: 空树的深度是0。样例 2:
输入: tree = {1,2,3,#,#,4,5}输出: 3样例解释: 树表示如下,深度是3 1 / \ 2 3 / \ 4 5它将被序列化为{1,2,3,#,#,4,5}

// Version 1: Divide Conquerpublic class Solution { public int maxDepth(TreeNode root) { if (root == null) { return 0; }
int left = maxDepth(root.left); int right = maxDepth(root.right); return Math.max(left, right) + 1; }}
// version 2: Traverse/** * Definition of TreeNode: * public class TreeNode { * public int val; * public TreeNode left, right; * public TreeNode(int val) { * this.val = val; * this.left = this.right = null; * } * } */public class Solution { /** * @param root: The root of binary tree. * @return: An integer. */ private int depth; public int maxDepth(TreeNode root) { depth = 0; helper(root, 1); return depth; } private void helper(TreeNode node, int curtDepth) { if (node == null) { return; } if (curtDepth > depth) { depth = curtDepth; } helper(node.left, curtDepth + 1); helper(node.right, curtDepth + 1); }}

二叉树的所有路径 · Binary Tree Paths

给一棵二叉树,找出从根节点到叶子节点的所有路径。
样例样例 1:
输入:{1,2,3,#,5}输出:["1->2->5","1->3"]解释: 1 / \2 3 \ 5样例 2:
输入:{1,2}输出:["1->2"]解释: 1 / 2
// version 1: Divide Conquerpublic class Solution { /** * @param root the root of the binary tree * @return all root-to-leaf paths */ public List<String> binaryTreePaths(TreeNode root) { List<String> paths = new ArrayList<>(); if (root == null) { return paths; }  List<String> leftPaths = binaryTreePaths(root.left); List<String> rightPaths = binaryTreePaths(root.right); for (String path : leftPaths) { paths.add(root.val + "->" + path); } for (String path : rightPaths) { paths.add(root.val + "->" + path); }  // root is a leaf if (paths.size() == 0) { paths.add("" + root.val); }  return paths; }}

Traverse + Divide Conquer

最小子树 · Minimum Subtree

给一棵二叉树, 找到和为最小的子树, 返回其根节点。输入输出数据范围都在int内。
LintCode会打印根节点为你返回节点的子树。保证只有一棵和最小的子树并且给出的二叉树不是一棵空树。
样例样例 1:
输入:{1,-5,2,1,2,-4,-5}输出:1说明这棵树如下所示: 1 / \ -5 2 / \ / \1 2 -4 -5 整颗树的和是最小的,所以返回根节点1.样例 2:
输入:{1}输出:1说明:这棵树如下所示: 1这棵树只有整体这一个子树,所以返回1.
/** * Definition of TreeNode: * public class TreeNode { * public int val; * public TreeNode left, right; * public TreeNode(int val) { * this.val = val; * this.left = this.right = null; * } * } */
public class Solution { private int minSum; private TreeNode minRoot; /** * @param root: the root of binary tree * @return: the root of the minimum subtree */ public TreeNode findSubtree(TreeNode root) { minSum = Integer.MAX_VALUE; minRoot = null; getSum(root); return minRoot; } private int getSum(TreeNode root) { if (root == null) { return 0; } int sum = getSum(root.left) + getSum(root.right) + root.val; if (sum < minSum) { minSum = sum; minRoot = root; } return sum; }}

平衡二叉树 · Balanced Binary Tree

给定一个二叉树,确定它是高度平衡的。对于这个问题,一棵高度平衡的二叉树的定义是:一棵二叉树中每个节点的两个子树的深度相差不会超过1。 
样例样例 1: 输入: tree = {1,2,3} 输出: true 样例解释: 如下,是一个平衡的二叉树。 1 / \ 2 3
样例 2: 输入: tree = {3,9,20,#,#,15,7} 输出: true 样例解释: 如下,是一个平衡的二叉树。 3 / \ 9 20 / \ 15 7
样例 2: 输入: tree = {1,#,2,3,4} 输出: false 样例解释: 如下,是一个不平衡的二叉树。1的左右子树高度差2 1 \ 2 / \ 3 4
class ResultType { public boolean isBalanced; public int maxDepth; public ResultType(boolean isBalanced, int maxDepth) { this.isBalanced = isBalanced; this.maxDepth = maxDepth; }}
public class Solution { /** * @param root: The root of binary tree. * @return: True if this Binary tree is Balanced, or false. */ public boolean isBalanced(TreeNode root) { return helper(root).isBalanced; } private ResultType helper(TreeNode root) { if (root == null) { return new ResultType(true, 0); } ResultType left = helper(root.left); ResultType right = helper(root.right); // subtree not balance if (!left.isBalanced || !right.isBalanced) { return new ResultType(false, -1); } // root not balance if (Math.abs(left.maxDepth - right.maxDepth) > 1) { return new ResultType(false, -1); } return new ResultType(true, Math.max(left.maxDepth, right.maxDepth) + 1); }}
// Version 2: without ResultTypepublic class Solution { public boolean isBalanced(TreeNode root) { return maxDepth(root) != -1; }
private int maxDepth(TreeNode root) { if (root == null) { return 0; }
int left = maxDepth(root.left); int right = maxDepth(root.right); if (left == -1 || right == -1 || Math.abs(left-right) > 1) { return -1; } return Math.max(left, right) + 1; }}

具有最大平均数的子树 · Subtree with Maximum Average

给一棵二叉树,找到有最大平均值的子树。返回子树的根结点。
LintCode会打印出根结点为你返回节点的子树,保证有最大平均数子树只有一棵
样例样例1
输入:{1,-5,11,1,2,4,-2}输出:11说明:这棵树如下所示: 1 / \ -5 11 / \ / \1 2 4 -2 11子树的平均值是4.333,为最大的。样例2
输入:{1,-5,11}输出:11说明: 1 / \ -5 111,-5,11 三棵子树的平均值分别是 2.333,-5,11。因此11才是最大的
// version 1: Traverse + Divide Conquerpublic class Solution { private class ResultType { public int sum, size; public ResultType(int sum, int size) { this.sum = sum; this.size = size; } }  private TreeNode subtree = null; private ResultType subtreeResult = null;  /** * @param root the root of binary tree * @return the root of the maximum average of subtree */ public TreeNode findSubtree2(TreeNode root) { helper(root); return subtree; }  private ResultType helper(TreeNode root) { if (root == null) { return new ResultType(0, 0); } // 分治法计算左右子树的平均值 ResultType left = helper(root.left); ResultType right = helper(root.right); // 当前subtree的结果是左右两颗子树的和的平均值加上自身 ResultType result = new ResultType( left.sum + right.sum + root.val, left.size + right.size + 1 ); // 打擂台比较得到最大平均值的子树 if (subtree == null || subtreeResult.sum * result.size < result.sum * subtreeResult.size ) { subtree = root; subtreeResult = result; } return result; }}

最近公共祖先 · Lowest Common Ancestor

描述给定一棵二叉树,找到两个节点的最近公共父节点(LCA)。
最近公共祖先是两个节点的公共的祖先节点且具有最大深度。
LCAVersion : Divide & Conquer
public class Solution { // 在root为根的二叉树中找A,B的LCA: // 如果找到了就返回这个LCA // 如果只碰到A,就返回A // 如果只碰到B,就返回B // 如果都没有,就返回null public TreeNode lowestCommonAncestor(TreeNode root, TreeNode node1, TreeNode node2) { if (root == null || root == node1 || root == node2) { return root; } // Divide TreeNode left = lowestCommonAncestor(root.left, node1, node2); TreeNode right = lowestCommonAncestor(root.right, node1, node2); // Conquer if (left != null && right != null) { return root; } if (left != null) { return left; } if (right != null) { return right; } return null; }}

二叉树的中序遍历 · Binary Tree Inorder Traversal

public class Solution { /** * @param root: The root of binary tree. * @return: Inorder in ArrayList which contains node values. */ public ArrayList<Integer> inorderTraversal(TreeNode root) { Stack<TreeNode> stack = new Stack<TreeNode>(); ArrayList<Integer> result = new ArrayList<Integer>(); TreeNode curt = root; while (curt != null || !stack.empty()) { while (curt != null) { stack.add(curt); curt = curt.left; } curt = stack.pop(); result.add(curt.val); curt = curt.right; } return result; }}

最通用的版本
考点:
二叉树的中序遍历(非递归)题解:首先访问左子树,将左子树存入栈中,每次将栈顶元素存入结果,如果右子树为空,取出栈顶元素,如果当前元素为栈顶元素右子树,一直弹出至当前元素不为栈顶元素右子树(此处说明访问右子树,根节点已经被访问过,弹出即可)。如果节点右子树不为空,访问右子树,继续循环遍历左子树,存入栈中。
public class Solution { /** * @param root: The root of binary tree. * @return: Inorder in ArrayList which contains node values. */ public ArrayList<Integer> inorderTraversal(TreeNode root) { Stack<TreeNode> stack = new Stack<>(); ArrayList<Integer> result = new ArrayList<>();  while (root != null) { stack.push(root); root = root.left; }  while (!stack.isEmpty()) { TreeNode node = stack.peek(); result.add(node.val);  if (node.right == null) { node = stack.pop(); while (!stack.isEmpty() && stack.peek().right == node) { node = stack.pop(); } } else { node = node.right; while (node != null) { stack.push(node); node = node.left; } } } return result; }}

二叉树的后序遍历 · Binary Tree Postorder Traversal

//Recursivepublic ArrayList<Integer> postorderTraversal(TreeNode root) { ArrayList<Integer> result = new ArrayList<Integer>();
if (root == null) { return result; }
result.addAll(postorderTraversal(root.left)); result.addAll(postorderTraversal(root.right)); result.add(root.val);
return result; }
//Iterativepublic ArrayList<Integer> postorderTraversal(TreeNode root) { ArrayList<Integer> result = new ArrayList<Integer>(); Stack<TreeNode> stack = new Stack<TreeNode>(); TreeNode prev = null; // previously traversed node TreeNode curr = root;
if (root == null) { return result; }
stack.push(root); while (!stack.empty()) { curr = stack.peek(); if (prev == null || prev.left == curr || prev.right == curr) { // traverse down the tree if (curr.left != null) { stack.push(curr.left); } else if (curr.right != null) { stack.push(curr.right); } } else if (curr.left == prev) { // traverse up the tree from the left if (curr.right != null) { stack.push(curr.right); } } else { // traverse up the tree from the right result.add(curr.val); stack.pop(); } prev = curr; }
return result;}

二叉树的后序遍历 题解: 使用栈进行二叉树后序遍历,首先对左子树进行遍历压入栈中,直至左子树为空,然后访问右子树。故每个节点会被访问两次,当节点被第二次访问时,该节点出栈。


以上是关于二叉树与分治法算法的主要内容,如果未能解决你的问题,请参考以下文章

第三节.二叉树和分治法

第5章 树与二叉树

树与二叉树的转换与遍历

考研数据结构与算法树与二叉树

二叉树 + 递归 + 分治法总结

树与二叉树