二叉树与分治法算法
Posted 每日算法之刻意练习
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉树与分治法算法相关的知识,希望对你有一定的参考价值。
二叉树的前序遍历 · Binary Tree Preorder Traversal
二叉树的前序遍历
题解:非递归方式实现前序遍历时,首先存入当前节点值,
然后先将右儿子压入栈中,再将左儿子压入栈中。对栈中元素遍历访问。
给出一棵二叉树,返回其节点值的前序遍历。
首个数据为根节点,后面接着是其左儿子和右儿子节点值,"#"表示不存在该子节点。
节点数量不超过20
样例
样例 1:
输入:
二叉树 = {1,2,3}
输出:
[ ]
解释:
1
/ \
2 3
它将被序列化为{1,2,3}前序遍历
样例 2:
输入:
二叉树 = {1,
输出:
[ ]
解释:
1
\
2
/
3
它将被序列化为{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 {
/**
* @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: Traverse
public 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 & Conquer
public 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 Conquer
public 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 Conquer
public 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:
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 ResultType
public 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 11
三棵子树的平均值分别是 2.333,-5,11。因此11才是最大的
// version 1: Traverse + Divide Conquer
public 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)。
最近公共祖先是两个节点的公共的祖先节点且具有最大深度。
LCA
Version : 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
//Recursive
public 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;
}
//Iterative
public 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;
}
二叉树的后序遍历 题解: 使用栈进行二叉树后序遍历,
首先对左子树进行遍历压入栈中,直至左子树为空,然后访问右子树。
故每个节点会被访问两次,当节点被第二次访问时,该节点出栈。
以上是关于二叉树与分治法算法的主要内容,如果未能解决你的问题,请参考以下文章