算法总结二叉树常见算法题目及解题思路汇总

Posted 在路上的德尔菲

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法总结二叉树常见算法题目及解题思路汇总相关的知识,希望对你有一定的参考价值。

二叉树算法总结

主要解决思路:

  • 递归
  • 自底向上分治
  • 栈或队列

常见算法题

二叉树结构定义如下

 public class TreeNode {
     int val;
     TreeNode left;
     TreeNode right;
     TreeNode(int x) { val = x; }
 }

1、前序遍历 LeetCode144

给定一个二叉树,返回它的 前序 遍历。

递归解法

public List<Integer> preorderTraversal(TreeNode root) {
	ArrayList list = new ArrayList<Integer>();
  preorderHelper(root, list);
  return list;
}
public void preorderHelper(TreeNode root, ArrayList list){
  if (root == null)return;
  list.add(root.val);
  preorderHelper(root.left, list);
  preorderHelper(root.right, list);
}

**注意点:**注意判空

迭代解法:

压入栈

public List<Integer> preorderTraversal(TreeNode root) {
	ArrayList<Integer> list = new ArrayList<Integer>();
  Stack<TreeNode> stack = new Stack<TreeNode>();
  if (root == null)return list;
  stack.push(root);
  while(!stack.isEmpty()){
    TreeNode visit = stack.pop();
    list.add(visit.val);
    if (root.right != null)stack.push(root.right);
    if (root.left != null)stack.push(root.left);
  }
  return list;
}

序列化二叉树 #297

你可以将以下二叉树:

    1
   / \\
  2   3
     / \\
    4   5

序列化为 “[1,2,3,null,null,4,5]”

public String serialize(TreeNode root) {
  StringBuilder res = helper(root, new StringBuilder());
  return res.toString();
}

private StringBuilder helper(TreeNode node, StringBuilder sb){
  if(node == null){
    sb.append("null,");
    return sb;
  }
  sb.append(node.val);
  sb.append(",");
  sb = helper(node.left, sb);
  sb = helper(node.right, sb);
  return sb; 
}

    // Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
  String[] array = data.split(",");
  List<String> list = new LinkedList<>(Arrays.asList(array));
  return rebuildTree(list);
}
private TreeNode rebuildTree(List<String> list){
  if(list.get(0).equals("null")){
    list.remove(0);
    return null;
  }
  TreeNode node = new TreeNode(Integer.valueOf(list.get(0)));
  list.remove(0);
  node.left = rebuildTree(list);
  node.right = rebuildTree(list);
  return node;
}

2、中序遍历94

递归方法和前序遍历类似,只是优先展示左子树的值

public List<Integer> inorderTraversal(TreeNode root) {
	ArrayList list = new ArrayList<Integer>();
  inorderHelper(root, list);
  return list;
}
public void inorderHelper(TreeNode root, ArrayList list){
  if (root == null)return;
  inorderHelper(root.left, list);
  list.add(root.val);
  inorderHelper(root.right, list);
}

迭代方法

public List<Integer> inorderTraversal(TreeNode root) {
	ArrayList<Integer> list = new ArrayList<Integer>();
  Stack<TreeNode> stack = new Stack<TreeNode>();
  TreeNode cur = root;
  while (cur != null || !stack.isEmpty()){
    while (cur != null){
      stack.push(cur);
      cur = cur.left;
    }
    cur = stack.pop();
    list.add(cur.val);
		cur = cur.right;
  }
  return list;
}

3、后续遍历#145

public List<Integer> postorderTraversal(TreeNode root) {
  ArrayList list = new ArrayList<Integer>();
  postorderHelper(root, list);
  return list;
}
public void postorderHelper(TreeNode root, ArrayList list){
  if (root == null)return;
  postorderHelper(root.left, list);
  postorderHelper(root.right, list);
  list.add(root.val);
}

注意点:

4、层序遍历 #102

即逐层地,从左到右访问所有节点

Input:

    3
   / \\
  9  20
    /  \\
   15   7

Output: [[3],[9,20],[15,7]]

ArrayList<ArrayList<Integer>> Print(BinaryTreeNode pRoot) {
  List<List<Integer>> res = new ArrayList<>();
  if (root == null) return res;

  ArrayList<Integer> list = new ArrayList<Integer>();
  Queue<TreeNode> queue = new LinkedList<TreeNode>();
  queue.offer(root);
  int start = 0, end = 1;
  while (!queue.isEmpty()) {
    //先进先出,从左到右
    TreeNode node = queue.poll();
    list.add(node.val);
    start++;
    if (node.left != null) queue.offer(node.left);
    if (node.right != null) queue.offer(node.right);
    //行控制 eg:end=1->2->4
    if (start == end) {
      end = queue.size();
      start = 0;
      res.add(list);
      list = new ArrayList<Integer>();
    }
  }
     return res;
}

5、打印Z字型 #面试题32

请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

使用两个栈,其中一个栈(stack2)处理从左到右打印,另一个栈处理从右到左打印,stack2先放左子树再放右子树到stack中,这样stack先取右子树再取左子树,同理stack入栈顺序相反,完成Z字型打印

public List<List<Integer>> levelOrder(TreeNode root) {
  List<List<Integer>> res = new ArrayList<>();
  ArrayList<Integer> list = new ArrayList<Integer>();
  if (root == null) return res;
  int start = 0, end = 1;
  Stack<TreeNode> stack = new Stack<>();
  Stack<TreeNode> stack2 = new Stack<>();
  stack2.push(root);
  while (!stack.isEmpty() || !stack2.isEmpty()) {
    while (!stack2.isEmpty()) {
      TreeNode qNode = stack2.pop();
      start++;
      list.add(qNode.val);
      if (qNode.left != null) stack.push(qNode.left);
      if (qNode.right != null) stack.push(qNode.right);
      if (start == end) {
        end = stack.size();
        start = 0;
        res.add(list);
        list = new ArrayList<Integer>();
      }
    }
    while (!stack.isEmpty()) {
      TreeNode sNode = stack.pop();
      start++;
      list.add(sNode.val);
      if (sNode.right != null) stack2.push(sNode.right);
      if (sNode.left != null) stack2.push(sNode.left);
      if (start == end) {
        end = stack2.size();
        start = 0;
        res.add(list);
        list = new ArrayList<Integer>();
      }
    }
  }

  return res;
}

5、包含子树

树A

    3
   / \\
  9  20
    /  \\
   15   7

子树B

    3         20
   / \\         \\
  9  20         7
public boolean isSubStructure(TreeNode A, TreeNode B) {
  if (A == null || B == null)return false;
  //从根开始匹配 左子树开始 右子树开始 
  return dfs(A, B) || isSubStructure(A.left,B) || isSubStructure(A.right,B);
}

public boolean dfs(TreeNode A, TreeNode B){
  if(B == null) return true;
  if(A == null) return false;
  return A.val == B.val && dfs(A.left, B.left) && dfs(A.right, B.right);
}

6、镜像树 #面试题27
输入:

    3
   / \\
  9  20
    /  \\
   15   7   

输出:

    3
   / \\
  20  9
 /  \\
7   15  
public TreeNode mirrorTree(TreeNode root) {
  if (root == null) return root;
  //叶子节点
  if (root.left == null && root.right == null) return root;
  //左右互换
  TreeNode temp = root.left;
  root.left = root.right;
  root.right = temp;
  if (root.left != null) mirrorTree(root.left);
  if (root.right != null) mirrorTree(root.right);
  return root;
}

6、对称树 #面试题28

如果一棵二叉树和它的镜像一样,那么它是对称的

public boolean isSymmetric(TreeNode root) {
  return root == null ? true : isSymmetric(root.left, root.right);
}

public boolean isSymmetric(TreeNode L, TreeNode R){
  if(L == null && R == null)return true;
  //左右子树一个存在一个不存在,或左右子树值不相等
  if(L == null || R == null || L.val != R.val){
    return false;
  }
  return isSymmetric(L.left,R.right) && isSymmetric(L.right,R.left);
}

7、路径和 #112

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

public boolean hasPathSum(TreeNode root, int sum) {
  if(root == null) return false;
  if(root.left == null && root.right == null && sum == root.val ) return true;
  return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
}

变形:给定一棵二叉树,其中每个节点都含有一个整数数值(该值或正或负)。设计一个算法,打印节点数值总和等于某个给定值的所有路径的数量。注意,路径不一定非得从二叉树的根节点或叶节点开始或结束。

Input : 22

          5
         / \\
        4   8
       /   / \\
      11  13  4
     /  \\    / \\
    7    2  5   1

Output : 3 [5,4,11,2]、[5,8,4,5]、[4,11,7]

public int pathSum(TreeNode root, int sum) {
  if(root == null)return 0;
  // 包含子树
  return helper(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum);
}

private int helper(TreeNode node, int sum){
  if (null == node){
    return 0;
  }
  sum -= node.val;
  int count = sum == 0 ? 1 : 0;
  count += helper(node.left, sum);
  count += helper(node.right, sum);
  return count;
}

8.1树最小深度

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。