二叉树


树里的每一个节点有一个值和一个包含所有子节点的列表。树也可视为一个拥有N 个节点和N-1 条边的有向无环图。

二叉树是一种典型的树状结构。如名字所述,二叉树是每个节点最多有两个子树的树结构,通常子树被称作“左子树”和“右子树”。

树的遍历有3中,前序遍历,中序遍历,后续遍历。

如上图:

前序遍历结果为: FBADCEFGIH

前序遍历代码:

方法1: 递归

class Solution {     public List preorderTraversal(TreeNode root) {         List result = new ArrayList<>();
        preorder(root, result);
        return result;     }
    private void preorder(TreeNode root, List result) {         if(root == null) {             return;         }         result.add(root.val);         preorder(root.left, result);         preorder(root.right, result);     } }

方法二: 迭代

class Solution {     public List preorderTraversal(TreeNode root) {         List result = new ArrayList<>();         if(root == null) {             return result;         }         ArrayDeque stack = new ArrayDeque<>();
        while(!stack.isEmpty() || root != null) {             while(root != null){                 result.add(root.val);                 stack.push(root);                 root = root.left;             }             root = stack.pop();             root = root.right;         }         return result;     } }

中序遍历结果为:ABCDEFGHI

中序遍历代码:

方法1: 递归

class Solution {     public List inorderTraversal(TreeNode root) {         List result = new ArrayList<>();
        inoreder(root, result);         return result;     }
    private void inoreder(TreeNode root, List result) {         if(root == null) {             return;         }         inoreder(root.left, result);         result.add(root.val);         inoreder(root.right, result);     } }  

后续遍历结果为:ACEDBHIGF

序遍历代码:

方法1: 递归

class Solution {     public List postorderTraversal(TreeNode root) {         List result = new ArrayList<>();
        postoreder(root, result);         return result;     }
    private void postoreder(TreeNode root, List result) {         if(root == null) {             return;         }         postoreder(root.left, result);         postoreder(root.right, result);         result.add(root.val);     } }     层序遍历:FBGADICEH   层序遍历就是一个广度优先的遍历(BFS)。   class Solution {     public List> levelOrder(TreeNode root) {         if(root == null){             return new ArrayList();         }         List> result = new ArrayList<>();
        LinkedList queue = new LinkedList<>();         queue.add(root);
        while(!queue.isEmpty()) {                    int size = queue.size();             List list = new ArrayList();             for(int i = 0; i < size; i++) {                 TreeNode node = queue.poll();                 list.add(node.val);                 if(node.left != null) {                     queue.add(node.left);                 }                 if(node.right != null) {                     queue.add(node.right);                 }                          }             result.add(list);         }
        return result;     } }   1.  对称二叉树  

给定一个二叉树,检查它是否是镜像对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

    1
   /   \
 2     2
/  \   /  \
3 4 4  3
 

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

1
/ \
2 2
\   \
3   3

作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/data-structure-binary-tree/xoxzgv/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

解题思路:递归左子树,右子树,进行比较。

class Solution {     public boolean isSymmetric(TreeNode root) {         if(root == null) {             return true;         }
        return isSymmetric(root.left, root.right);     }
    private boolean isSymmetric(TreeNode left, TreeNode right) {         if(left == null && right == null) {             return true;         }         if(left == null || right == null || left.val != right.val) {             return false;         }
        return isSymmetric(left.left, right.right) && isSymmetric(left.right, right.left);     } }     2. 路径总和  

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

说明: 叶子节点是指没有子节点的节点。

示例: 
给定如下二叉树,以及目标和 sum = 22,

         5
       /    \
     4     8
    /      /   \
  11    13  4
 /   \            \
7    2           1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。

作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/data-structure-binary-tree/xo566j/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    class Solution {     public boolean hasPathSum(TreeNode root, int sum) {              if(root == null) {             return false;         }
        if(root.left == null && root.right == null) {             return sum == root.val;         }         sum -= root.val;         return hasPathSum(root.left, sum) || hasPathSum(root.right, sum);     } }   3. 从中序与后序遍历序列构造二叉树

根据一棵树的中序遍历与后序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:

       3
     /    \
   9     20
         /   \
       15   7

作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/data-structure-binary-tree/xo98qt/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

/** * Definition for a binary tree node.

 * public class TreeNode {  *     int val;  *     TreeNode left;  *     TreeNode right;  *     TreeNode(int x) { val = x; }  * }  */ class Solution {     Map valIndexMap = new HashMap<>();     int postIndex;
    public TreeNode buildTree(int[] inorder, int[] postorder) {         if(inorder.length == 0) {             return null;         }           //后序遍历的最后一个节点为根节点。         postIndex = postorder.length - 1;
        for(int i = 0; i < inorder.length; i++) {             valIndexMap.put(inorder[i], i);         }
        return buildTree(inorder, postorder, 0, postIndex);     }
    private TreeNode buildTree(int[] inorder, int[] postorder, int leftIndex, int rightIndex) {         if(leftIndex > rightIndex) {             return null;         }     
        int val = postorder[postIndex];         TreeNode root = new TreeNode(val);   //中序遍历该节点的位子。该位置的右侧都为右子树,左侧都为左子树。
        int index = valIndexMap.get(val);      //后序遍历往前移一位。
        postIndex--;   //构建右子树。         root.right = buildTree(inorder, postorder, index + 1, rightIndex);   //构建左子树         root.left = buildTree(inorder, postorder, leftIndex, index - 1);         return root;     } }   4. 填充每个节点的下一个右侧节点指针

给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

初始状态下,所有 next 指针都被设置为 NULL。

进阶:

你只能使用常量级额外空间。
使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
 

示例:

输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,'#' 标志着每一层的结束。

作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/data-structure-binary-tree/xoo0ts/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

解题思路:使用BFS, 按层次进行遍历,从左往右链接起来。
class Solution {     public Node connect(Node root) {         if(root == null) {             return null;         }         LinkedList queue = new LinkedList<>();         queue.add(root);
        while(!queue.isEmpty()) {             int size = queue.size();             for(int i = 0; i < size; i++) {                 Node node = queue.poll();                 if(i < size - 1) {                     node.next = queue.peek();                 }                 if(node.left != null) {                     queue.add(node.left);                 }                 if(node.right != null) {                     queue.add(node.right);                 }             }         }
        return root;
    } }   5. 二叉树的序列化与反序列化  

序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。

请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。

示例: 

你可以将以下二叉树:

           1
         /   \
       2     3
            /   \
          4     5

序列化为 "[1,2,3,null,null,4,5]"
提示: 这与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。

说明: 不要使用类的成员 / 全局 / 静态变量来存储状态,你的序列化和反序列化算法应该是无状态的。

相关标签

作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/data-structure-binary-tree/xomr73/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  public class Codec {
    // Encodes a tree to a single string.     public String serialize(TreeNode root) {         StringBuilder sb = new StringBuilder();         LinkedList queue = new LinkedList<>();         queue.add(root);
        while(!queue.isEmpty()) {             int size = queue.size();             for(int i = 0; i < size; i++) {                 TreeNode node = queue.poll();                 if(node == null){                     sb.append("null,");                 } else {                     sb.append(node.val + ",");                     queue.add(node.left);                     queue.add(node.right);                 }             }         }         return sb.deleteCharAt(sb.length() - 1).toString();     }
    // Decodes your encoded data to tree.     public TreeNode deserialize(String data) {         if(data == null || "".equals(data) || data.startsWith("null")) {             return null;         }         String[] nodes = data.split(",");         LinkedList queue = new LinkedList<>();         TreeNode root = new TreeNode(Integer.valueOf(nodes[0]));         queue.add(root);
        for(int i = 1; i < nodes.length; i = i+2) {             TreeNode node = queue.poll();             if(!"null".equals(nodes[i])){                 TreeNode left = new TreeNode(Integer.valueOf(nodes[i]));                 node.left = left;                 queue.add(left);             }             if(!"null".equals(nodes[i+1])) {                 TreeNode right = new TreeNode(Integer.valueOf(nodes[i + 1]));                 node.right = right;                 queue.add(right);             }         }
        return root;     } }   6. 树的子结构

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

B是A的子结构, 即 A中有出现和B相同的结构和节点值。

例如:
给定的树 A:

       3
     /   \
   4     5
  /  \
1    2
给定的树 B:

   4 
  /
 1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。

示例 1:

输入:A = [1,2,3], B = [3,1]
输出:false
示例 2:

输入:A = [3,4,5,1,2], B = [4,1]
输出:true

作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5dshwe/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

解题思路: 简单暴力,从树的根节点往下比较,遍历不匹配,则从左子树遍历,再从右子树遍历。

class Solution {     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);     }
    private boolean dfs(TreeNode A, TreeNode B) {         if(B == null) {             return true;         }         if(A == null || A.val != B.val) {             return false;         }         return dfs(A.left, B.left) && dfs(A.right, B.right);     } }  

7. 二叉树的锯齿形层序遍历

给定一个二叉树,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

例如:
给定二叉树 [3,9,20,null,null,15,7],

3
/ \
9 20
/ \
15 7

返回锯齿形层序遍历如下:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路: 一层一层遍历,一层节点遍历完后,添加一个null节点,这个null节点用来辅助判断一层已经遍历完成。

class Solution {
    public List> zigzagLevelOrder(TreeNode root) {
        if(root == null){
            return new ArrayList<>();
        }
        List> result = new ArrayList<>();
        LinkedList nodeQueue = new LinkedList<>();
        nodeQueue.addLast(root);
        nodeQueue.addLast(null);

        LinkedList levelList = new LinkedList<>();
        boolean isLeftOrder = true;

        while(nodeQueue.size() > 0){
            TreeNode currNode = nodeQueue.pollFirst();

            if(currNode != null){
                if(isLeftOrder){
                    levelList.addLast(currNode.val);
                }else{
                    levelList.addFirst(currNode.val);
                }
                
                if(currNode.left != null){
                    nodeQueue.addLast(currNode.left);
                }
                if(currNode.right != null){
                    nodeQueue.addLast(currNode.right);
                }
            } else{
                result.add(levelList);
                levelList = new LinkedList<>();
                
                if(nodeQueue.size() > 0){
                    nodeQueue.addLast(null);
                }
                isLeftOrder = !isLeftOrder;
            }
        }

        return result;
    }
}

8. 二叉树的最近公共祖先


给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。


百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”


例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]


示例 1:


输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。


示例 2:


输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。


来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法一:

class Solution {
   public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {

        Stack path = new Stack<>();
        Stack node_p_path = new Stack<>();
        Stack node_q_path = new Stack<>();
        boolean found = false;

        preorder(root, path, node_p_path, p, found);
        path = new Stack<>();
        found = false;
        preorder(root,path, node_q_path, q, found);

        int length = 0;
        if(node_p_path.size() < node_q_path.size()){
            length = node_p_path.size();
            while(node_q_path.size() > length){
                node_q_path.pop();
            }
        }
        else{
            length = node_q_path.size();
            while(node_p_path.size() > length){
                node_p_path.pop();
            }
        }

        TreeNode result = null;

        for(int i = 0; i < length; i++){
            if(node_p_path.peek().equals(node_q_path.peek())){
                return new TreeNode(node_p_path.pop());
            }else
            {
                node_p_path.pop();
                node_q_path.pop();
            }
        }

        return result;
    }

    private void preorder(TreeNode root, Stack path, Stack node_p_path, TreeNode p, boolean found) {

        if(root == null || found){
            return;
        }
        path.push(root.val);

        if(root == p){
            found = true;
            node_p_path.addAll(path);
        }

        preorder(root.left, path, node_p_path, p, found);
        preorder(root.right,path, node_p_path, p, found);

        path.pop();
    }
}

方法2:

class Solution {
Map parent = new HashMap();
Set visited = new HashSet();

public void dfs(TreeNode root) {
      if (root.left != null) {
             parent.put(root.left.val, root);
             dfs(root.left);
      }
     if (root.right != null) {
           parent.put(root.right.val, root);
          dfs(root.right);
     }
}

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
      dfs(root);
      while (p != null) {
           visited.add(p.val);
           p = parent.get(p.val);
      }
     while (q != null) {
         if (visited.contains(q.val)) {
         return q;
         }
         q = parent.get(q.val);
      }
     return null;
   }
}

方法三:

public class FindLowestAncestor {
  public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if(root == null) return null;
    if(root == p || root == q) {
      return root;
    }
    TreeNode left = lowestCommonAncestor(root.left, p, q);
    TreeNode right = lowestCommonAncestor(root.right, p, q);

    if(left != null && right != null) {
      return root;
    }
    if(left == null && right == null) {
      return null;
    }

    return left == null ? right : left;
  }
}

687. 最长同值路径

难度中等

给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。

注意:两个节点之间的路径长度由它们之间的边数表示。

示例 1:

输入:

              5
             / \
            4   5
           / \   \
          1   1   5

输出:

2

示例 2:

输入:

              1
             / \
            4   5
           / \   \
          4   4   5

输出:

2

class Solution {
    int maxLines = 0;
    public int longestUnivaluePath(TreeNode root) {
        dfs(root);
        return maxLines;
    }

    private int dfs(TreeNode root) {
        if(root == null || (root.left == null && root.right == null)) {
            return 0;
        }
        
        int leftCount = dfs(root.left);
        int rightCount = dfs(root.right);

        if(root.left != null) {
            if(root.left.val == root.val) {
                leftCount++;
            } else {
                leftCount = 0;
            }
        }
        if(root.right != null) {
            if(root.right.val == root.val) {
                rightCount++;
            } else {
                rightCount = 0;
            }
        }
        maxLines = Math.max(maxLines, leftCount + rightCount);
        return Math.max(leftCount, rightCount);
    }
}