树相关算法题


重建二叉树

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

牛客网

思路分析

根据中序遍历和前序遍历可以确定二叉树,具体过程为:

  • 根据前序序列第一个结点确定根结点
  • 根据根结点在中序序列中的位置分割出左右两个子序列
  • 对左子树和右子树分别递归使用同样的方法继续分解‘

例如:

前序序列{1,2,4,7,3,5,6,8} = pre

中序序列{4,7,2,1,5,3,8,6} = in

1、根据当前前序序列的第一个结点确定根结点,为 1

2、找到 1 在中序遍历序列中的位置,为 in[3]

3、切割左右子树,则 in[3] 前面的为左子树, in[3] 后面的为右子树

4、则切割后的左子树前序序列为:{2,4,7},切割后的左子树中序序列为:{4,7,2};切割后的右子树前序序列为:{3,5,6,8},切割后的右子树中序序列为:{5,3,8,6}

5、对子树分别使用同样的方法分解

代码实现

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
import java.util.Arrays;
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if (pre.length == 0 || in.length == 0) {
            return null;
        }
        TreeNode root = new TreeNode(pre[0]);
        for (int i=0; i<in.length; i++) {
        // 在中序中找到前序的根
            if (in[i] == pre[0]) {
                // 左子树,注意 copyOfRange 函数,左闭右开
                root.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i+1), Arrays.copyOfRange(in, 0, i));
                // 右子树,注意 copyOfRange 函数,左闭右开
                root.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i+1, pre.length), Arrays.copyOfRange(in, i+1, in.length));
                break;
            }
        }
        return root;
    }
}

复杂度分析

时间复杂度 O(n)
空间复杂度 O(n)

二叉树的下一个结点

题目描述

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

牛客网

public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;  #指向父结点的指针

    TreeLinkNode(int val) {
        this.val = val;
    }
}

解法1 还原二叉树

思路分析

既然给了二叉树的某个结点,且二叉树存储着指向父结点的指针(next),那我们可以先找到根节点,再对树进行中序遍历,最后根据中序遍历结果找到给定结点的下一结点。

代码实现

/*
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;

    TreeLinkNode(int val) {
        this.val = val;
    }
}
*/
import java.util.*;
public class Solution {
    static ArrayList<TreeLinkNode> list = new ArrayList();
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        TreeLinkNode par = pNode;
        //先找根结点
        while (par.next != null) {
            par = par.next;
        }
        //再进行中序遍历
        InOrder(par);
        //返回下一个结点
        for (int i=0; i<list.size(); i++) {
            if (pNode == list.get(i)) {
                return i == list.size()-1 ? null : list.get(i+1);
            }
        }
        return null;
    }

    //二叉树中序遍历
    void InOrder(TreeLinkNode pNode) {
        if (pNode != null) {
            InOrder(pNode.left);
            list.add(pNode);
            InOrder(pNode.right);
        }
    }
}

复杂度

时间复杂度 O(n)
空间复杂度 O(n)

解法2 直接找下一结点

思路分析1

以该二叉树为例,中序遍历为:{D,B,H,E,I,A,F,C,G}

二叉树

仔细观察,可以把中序下一结点归为几种类型:

有右子树,下一结点是右子树中的最左结点,例如 B,下一结点是 H。

无右子树,且结点是该结点父结点的左子树,则下一结点是该结点的父结点,例如 H,下一结点是 E。

无右子树,且结点是该结点父结点的右子树,则我们一直沿着父结点追朔,直到找到某个结点是其父结点的左子树,如果存在这样的结点,那么这个结点的父结点就是我们要找的下一结点。例如 I,下一结点是 A;例如 G,并没有符合情况的结点,所以 G 没有下一结点。

代码实现

/*
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;

    TreeLinkNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
     // 1.有右子树,下一结点是右子树中的最左结点
     if (pNode.right != null) {
         TreeLinkNode pRight = pNode.right;
         while (pRight.left != null) {
             pRight = pRight.left;
         }
         return pRight;
     }
     // 2.无右子树,且结点是该结点父结点的左子树,则下一结点是该结点的父结点
     if (pNode.next != null && pNode.next.left == pNode) {
         return pNode.next;
     }
     // 3.无右子树,且结点是该结点父结点的右子树,则我们一直沿着父结点追朔,直到找到
     // 某个结点是其父结点的左子树,如果存在这样的结点,那么这个结点的父结点就是我们要找的下一结点
     if (pNode.next != null) {
         TreeLinkNode pNext = pNode.next;
         while (pNext.next != null && pNext.next.right == pNext) {
             pNext = pNext.next;
         }
         return pNext.next;
     }
     return null;
    }
}

思路分析2

我们先来回顾一下中序遍历的过程:先遍历树的左子树,再遍历根节点,最后再遍历右子树。所以最左节点是中序遍历的第一个节点。

代码实现

public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if (pNode.right != null) {
            TreeLinkNode node = pNode.right;
            while (node.left != null) {
                node = node.left;               
            }
            return node;
        } else {     
            while (pNode.next != null) {
                 TreeLinkNode parent = pNode.next;
                if (parent.left == pNode) {
                    return parent;
                }
                pNode = pNode.next;
            }   
        }
        return null;
    }
}

复杂度

时间复杂度 O(n)
空间复杂度 O(1)

二叉树镜像

题目描述

操作给定的二叉树,将其变换为源二叉树的镜像。

牛客网

二叉树的镜像定义:源二叉树 
        8
       /  \
      6   10
     / \  / \
    5  7 9 11
    镜像二叉树
        8
       /  \
      10   6
     / \  / \
    11 9 7  5

思路分析

交换左右子树的节点,然后递归调用该方法。

代码实现

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public void Mirror(TreeNode root) {
        if (root == null) 
                return;
        swap(root);
        Mirror(root.left);
        Mirror(root.right);
    }

    private void swap(TreeNode root) {
        TreeNode node = root.left;
        root.left = root.right;
        root.right = node;
    }
}

对称的二叉树

题目描述

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

牛客网

解法1 递归

思路分析

根节点的左右子树相同,左子树的左子树和右子树的右子树相同,左子树的右子树和右子树的左子树相同即可。

代码实现

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    boolean isSymmetrical(TreeNode pRoot)
    {
        if (pRoot == null) {
            return true;
        }
        return isSymmetrical(pRoot.left, pRoot.right);
    }

    boolean isSymmetrical(TreeNode left, TreeNode right) {
        if (left == null && right == null) {
            return true;
        }
        if (left == null || right == null) {
            return false;
        }
        if (left.val == right.val) {
            return isSymmetrical(left.left, right.right) && isSymmetrical(left.right, right.left);
        }
        return false;
    }
}

解法2 非递归

思路分析

采用栈或队列存取各级子树根节点。

代码实现

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
import java.util.Stack;
public class Solution {
    boolean isSymmetrical(TreeNode pRoot)
    {
        if (pRoot == null) 
            return true;
        Stack<TreeNode> s = new Stack<TreeNode>();
        s.push(pRoot.left);
        s.push(pRoot.right);
        while (!s.isEmpty()) {
            TreeNode right = s.pop();
            TreeNode left = s.pop();
            if (right == null && left == null)
                continue;
            if (right == null || left == null)
                return false;
            if (right.val != left.val) 
                return false;
            s.push(left.left);
            s.push(right.right);
            s.push(left.right);
            s.push(right.left);
        }
        return true;
    }
}

104 二叉树的高度

题目描述

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

LeetCode

思路分析

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数,使用递归解决问题。使用DFS(深度优先搜索)策略。

代码实现

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
 import java.lang.Math;
class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null)
            return 0;
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}

复杂度

时间复杂度 O(N) N为结点数
空间复杂度 O(log(N))

110 平衡二叉树

题目描述

给定一个二叉树,判断它是否是高度平衡的二叉树。

一棵高度平衡二叉树定义为:一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1。

LeetCode

给定二叉树 [3,9,20,null,null,15,7]
 3
/ \
9  20
 /  \
15   7
返回 true 

解法1 自顶向下(暴力法)

构造一个获取当前节点最大深度的方法 depth(root) ,通过比较此子树的左右子树的最大高度差abs(depth(root.left) - depth(root.right)),来判断此子树是否是二叉平衡树。若树的所有子树都平衡时,此树才平衡。

此方法容易想到,但会产生大量重复计算,时间复杂度较高。

实现代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
        if (root == null) return true;
        return Math.abs(depth(root.left) - depth(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
    }

    private int depth(TreeNode root) {
        if (root == null) return 0;
        return Math.max(depth(root.left), depth(root.right)) + 1;
    }
}

复杂度分析

时间复杂度 O(nlgn)
空间复杂度 O(n)

解法2 自底向上

对二叉树做先序遍历,从底至顶返回子树最大高度,若判定某子树不是平衡树则 “剪枝” ,直接向上返回。

代码实现

class Solution {
    public boolean isBalanced(TreeNode root) {
        return recur(root) != -1;
    }

    private int recur(TreeNode root) {
        if (root == null) return 0;
        int left = recur(root.left);
        if (left == -1) return -1;
        int right = recur(root.right);
        if (right == -1) return -1;
        return Math.abs(left - right)<2 ? Math.max(left,right)+1 : -1;
    } 
}

复杂度分析

时间复杂度 O(n)
空间复杂度 O(n)

在IntelliJ IDEA中测试

package com.cn.algorithm;

/**
 * @Author: cyh
 * @Description: 平衡二叉树
 **/
public class isBalancedDemo {

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

    /**
     * 自顶向下
     * @param root
     * @return
     */
    public boolean isBalanced(TreeNode root) {
        if (root == null)
            return true;
        return Math.abs(depth(root.left) - depth(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
    }

    private int depth(TreeNode root) {
        if (root == null)
            return 0;
        return Math.max(depth(root.left), depth(root.right)) + 1;
    }

    /**
     * 自底向上
     * @param root
     * @return
     */
    public boolean isBalanced(TreeNode root) {
        return recur(root) != -1;
    }

    private int recur(TreeNode root) {
        if (root == null) return 0;
        int left = recur(root.left);
        if (left == -1) return -1;
        int right = recur(root.right);
        if (right == -1) return -1;
        return Math.abs(left - right)<2 ? Math.max(left,right)+1 : -1;
    }

    TreeNode arrayToTree(Integer[] array, int index) {
        TreeNode root = null;
        if (index < array.length) {
            Integer value = array[index];
            if (value == null) {
                return null;
            }
            root = new TreeNode(value);
            root.left = arrayToTree(array, 2*index + 1);
            root.right = arrayToTree(array, 2*index + 2);
            return root;
        }
        return root;
    }

    public static void main(String[] args) {
        isBalancedDemo demo = new isBalancedDemo();
        Integer[] arr = {3,9,20,null,null,15,7};
        TreeNode root = demo.arrayToTree(arr, 0);
        Boolean flag = demo.isBalanced(root);
        System.out.println(flag);
    }
}

输出结果:

true

543 二叉树的直径

题目描述

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。

LeetCode

给定二叉树 [1,2,3,4,5]
           1
      / \
     2   3
    / \     
   4   5    
返回 3 

解题思路(深度优先搜索)

定义一个全局变量 res,用来记录最大直径。使用 dfs(node) 遍历所有的节点。

dfs(node) 的作用是:找出以 node 为根节点的二叉树的最大深度,将根节点的深度定义为 1。res 取值为以经过 root,左右子树的最大深度之和 leftDepth + rigthDepth。通过递归,找到 res 的最大值。

实现代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int res = 0;
    public int diameterOfBinaryTree(TreeNode root) {
        dfs(root);
        return res;
    }

    private int dfs(TreeNode node) {
        if (node == null) return 0;
        int leftDepth = dfs(node.left);
        int rightDepth = dfs(node.right);
        res = Math.max(res, leftDepth + rightDepth);
        return Math.max(leftDepth, rightDepth) + 1;
    }
}

复杂度分析

时间复杂度 O(n),其中 n 为二叉树的节点数
空间复杂度 O(Height),其中 Height为二叉树的高度

由于递归函数在递归过程中需要为每一层递归函数分配栈空间,所以这里需要额外的空间且该空间取决于递归的深度,而递归的深度显然为二叉树的高度,并且每次递归调用的函数里又只用了常数个变量,所以所需空间复杂度为 O(Height)O(Height)

在IntelliJ IDEA中测试

package com.cn.algorithm;

/**
 * @Author: cyh
 * @Description: 二叉树的直径
 **/
public class DiameterOfBinaryTreeDemo {

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

    TreeNode arrayToTree(Integer[] array, int index) {
        TreeNode root = null;
        if (index < array.length) {
            Integer value = array[index];
            if (value == null) {
                return null;
            }
            root = new TreeNode(value);
            root.left = arrayToTree(array, 2*index + 1);
            root.right = arrayToTree(array, 2*index + 2);
            return root;
        }
        return root;
    }

    int res = 0;
    public int diameterOfBinaryTree(TreeNode root) {
        dfs(root);
        return res;
    }

    public int dfs(TreeNode node) {
        if (node == null) return 0;
        int leftDepth = dfs(node.left);
        int rightDepth = dfs(node.right);
        res = Math.max(res, leftDepth + rightDepth);
        return Math.max(leftDepth, rightDepth) + 1;
    }


    public static void main(String[] args) {
        DiameterOfBinaryTreeDemo demo = new DiameterOfBinaryTreeDemo();
        Integer[] arr = {1,2,3,4,5};
        TreeNode root = demo.arrayToTree(arr, 0);
        int r = demo.diameterOfBinaryTree(root);
        System.out.println(r);
    }
}

输出结果:

3

226 翻转二叉树

题目描述

翻转一棵二叉树。

LeetCode

输入:
  4
/   \
2     7
/ \   / \
1   3 6   9
输出:
  4
/   \
7     2
/ \   / \
9   6 3   1

解法1 递归法(深度优先遍历)

交换左右节点,然后再递归的交换左节点,右节点。

递归法

图片来源:https://leetcode-cn.com/problems/invert-binary-tree/solution/dong-hua-yan-shi-liang-chong-shi-xian-226-fan-zhua/

实现代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) return null;
        // 交换左右节点
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        // 递归
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
}

复杂度分析

时间复杂度 O(n),其中 n 为二叉树的节点数
空间复杂度 O(Height),其中 Height为二叉树的高度

解法2 迭代法(广度优先遍历)

先将根节点放入到队列中,然后不断的迭代队列中的元素。对当前元素调换其左右子树的位置,然后:

  • 判断其左子树是否为空,不为空就放入队列中
  • 判断其右子树是否为空,不为空就放入队列中

迭代法

图片来源:https://leetcode-cn.com/problems/invert-binary-tree/solution/dong-hua-yan-shi-liang-chong-shi-xian-226-fan-zhua/

代码实现

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) return null;
        //将二叉树中的节点逐层放入队列中,再迭代处理队列中的元素
        LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
        queue.add(root);
        while(!queue.isEmpty()) {
            //每次都从队列中拿一个节点,并交换这个节点的左右子树
            TreeNode tmp = queue.poll();
            TreeNode left = tmp.left;
            tmp.left = tmp.right;
            tmp.right = left;
            //如果当前节点的左子树不为空,则放入队列等待后续处理
            if(tmp.left!=null) {
                queue.add(tmp.left);
            }
            //如果当前节点的右子树不为空,则放入队列等待后续处理
            if(tmp.right!=null) {
                queue.add(tmp.right);
            }
        }
        //返回处理完的根节点
        return root;
    }
}

复杂度分析

时间复杂度 O(n)
空间复杂度 O(n)

在IntelliJ IDEA中测试

package com.cn.algorithm;

import java.util.*;

/**
 * @Author: cyh
 * @Description: 翻转二叉树
 **/
public class InvertTreeDemo {

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

    TreeNode arrayToTree(Integer[] array, int index) {
        TreeNode root = null;
        if (index < array.length) {
            Integer value = array[index];
            if (value == null) {
                return null;
            }
            root = new TreeNode(value);
            root.left = arrayToTree(array, 2*index + 1);
            root.right = arrayToTree(array, 2*index + 2);
            return root;
        }
        return root;
    }

    List<Integer> treeToArray(TreeNode root, List<Integer> list) {
        if (root == null) return null;
           list.add(root.val);

               if (root.left != null) {
                   list.add(root.left.val);
               }
               if (root.right != null) {
                   list.add(root.right.val);
               }
               treeToArray(root.left, list);
               treeToArray(root.right, list);
        return list;
    }


    /**
     * 递归法(深度优先遍历)
     * @param root
     * @return
     */
    public TreeNode invertTree(TreeNode root) {
        if (root == null) return null;
        // 交换左右节点
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        // 递归
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }

    /**
     * 迭代法(广度优先遍历)
     * @param root
     * @return
     */
    public TreeNode invertTree(TreeNode root) {
        if (root == null) return null;
        //将二叉树中的节点逐层放入队列中,再迭代处理队列中的元素
        LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
        queue.add(root);
        while(!queue.isEmpty()) {
            //每次都从队列中拿一个节点,并交换这个节点的左右子树
            TreeNode tmp = queue.poll();
            TreeNode left = tmp.left;
            tmp.left = tmp.right;
            tmp.right = left;
            //如果当前节点的左子树不为空,则放入队列等待后续处理
            if(tmp.left!=null) {
                queue.add(tmp.left);
            }
            //如果当前节点的右子树不为空,则放入队列等待后续处理
            if(tmp.right!=null) {
                queue.add(tmp.right);
            }
        }
        //返回处理完的根节点
        return root;
    }

    public static void main(String[] args) {
        InvertTreeDemo demo = new InvertTreeDemo();
        Integer[] arr = {4,2,7,1,3,6,9};
        TreeNode root = demo.arrayToTree(arr, 0);
        TreeNode invertTree = demo.invertTree(root);
        List<Integer> list = new ArrayList<>();
        list = demo.treeToArray(invertTree, list);
        System.out.println(list);
    }
}

输出结果:

[4, 7, 2, 7, 9, 6, 9, 6, 2, 3, 1, 3, 1]

94 二叉树的中序遍历

题目描述

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

LeetCode

输入: [1,null,2,3]
1
 \
  2
 /
3

输出: [1,3,2]

解法1 递归法

使用经典的递归法实现,编写一个辅助函数实现递归。

实现代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if (root == null) {
            return list;
        }
        helper(root, list);
        return list;
    }

    public void helper(TreeNode root, List<Integer> res) {
        if (root.left != null) {
            helper(root.left, res);
        }
        res.add(root.val);
        if (root.right != null) {
            helper(root.right, res);
        }
    }
}

复杂度分析

时间复杂度 O(n)
空间复杂度 最坏情况下需要空间O(n),平均情况为O(logn)

解法2 迭代法

基于栈的遍历

代码实现

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

复杂度分析

时间复杂度 O(n)
空间复杂度 O(n)

解法3 颜色标记法

使用颜色标记节点的状态,新节点为白色,已访问的节点为灰色。
如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点、自身、左子节点依次入栈。
如果遇到的节点为灰色,则将节点的值输出。

代码实现

class Solution {
    public class ColorNode {
        TreeNode root;
        String color;

       public ColorNode(TreeNode root, String color) {
            this.root = root;
            this.color = color;
        }
    }

    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Stack<ColorNode> stack = new Stack<>();
        if (root == null) return null;
        stack.push(new ColorNode(root, "white"));
        while (!stack.isEmpty() ) {
            ColorNode colorNode = stack.pop();
            if (colorNode.color.equals("white")) {
                if (colorNode.root.right != null) {
                    stack.push(new ColorNode(colorNode.root.right, "white"));
                }
                stack.push(new ColorNode(colorNode.root, "gray"));
                if (colorNode.root.left != null) {
                    stack.push(new ColorNode(colorNode.root.left, "white"));
                }
            } else {
                res.add(colorNode.root.val);
            }
        }
        return res;
    }
}

复杂度分析

时间复杂度 O(n)
空间复杂度 O(n)

在IntelliJ IDEA中测试

package com.cn.algorithm;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * @Author: cyh
 * @Description: 二叉树的中序遍历
 * 三种方法:1、递归法 2、迭代法 3、颜色标记法
 **/
public class inorderTraversalDemo {

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

    /**
     * 1.递归法
     * @param root
     * @return
     */
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        helper(root, res);
        return res;
    }

    public void helper(TreeNode root, List<Integer> res) {
        if (root != null) {
            if (root.left != null) {
                helper(root.left, res);
            }
            res.add(root.val);
            if (root.right != null) {
                helper(root.right, res);
            }
        }
    }

    /**
     * 2.非递归版
     * 用栈实现,迭代法
     * @param root
     * @return
     */
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        while ( root != null || !stack.empty()) {
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            res.add(root.val);
            root = root.right;
        }
        return res;
    }

    /**
     * 3.颜色标记法
     */
    public class ColorNode {
        TreeNode root;
        String color;

       public ColorNode(TreeNode root, String color) {
            this.root = root;
            this.color = color;
        }
    }

    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Stack<ColorNode> stack = new Stack<>();
        if (root == null) return null;
        stack.push(new ColorNode(root, "white"));
        while (!stack.isEmpty() ) {
            ColorNode colorNode = stack.pop();
            if (colorNode.color.equals("white")) {
                if (colorNode.root.right != null) {
                    stack.push(new ColorNode(colorNode.root.right, "white"));
                }
                stack.push(new ColorNode(colorNode.root, "gray"));
                if (colorNode.root.left != null) {
                    stack.push(new ColorNode(colorNode.root.left, "white"));
                }
            } else {
                res.add(colorNode.root.val);
            }
        }
        return res;
    }


    /**
     * 将数组转化为树结构
     * @param array
     * @param index
     * @return
     */
    TreeNode arrayToTree(Integer[] array, int index) {
        TreeNode root = null;
        if (index < array.length) {
            Integer value = array[index];
            if (value == null) {
                return null;
            }
            root = new TreeNode(value);
            root.left = arrayToTree(array, 2*index + 1);
            root.right = arrayToTree(array, 2*index + 2);
            return root;
        }
        return root;
    }

    public static void main(String[] args) {
        Integer[] arr = {1,null,2,null,null,3};
        inorderTraversalDemo it = new inorderTraversalDemo();
        TreeNode root = it.arrayToTree(arr, 0);
        List<Integer> list = it.inorderTraversal(root);
        System.out.println(list);
    }
}

输出结果:

[1, 3, 2]

94 合并二叉树

题目描述

给定两个二叉树,你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

LeetCode

输入: 
    Tree 1                     Tree 2                  
       1                         2                             
      / \                       / \                            
     3   2                     1   3                        
    /                           \   \                      
   5                             4   7                  
输出: 
合并后的树:
         3
        / \
       4   5
      / \   \ 
     5   4   7

解法1 递归法

前序遍历二叉树,再依次把访问到的节点值相加,题目没有说不能改变树的值和结构,我们不用再创建新的节点了,直接将树2合并到树1上再返回树就可以了。

recursion.gif

图片来源:https://leetcode-cn.com/problems/merge-two-binary-trees/solution/dong-hua-yan-shi-di-gui-die-dai-617he-bing-er-cha-/

实现代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
       if (t1 == null || t2 == null) {
           return t1 == null ? t2 : t1;
       }
       t1.val += t2.val;
       t1.left = mergeTrees(t1.left, t2.left);
       t1.right = mergeTrees(t1.right, t2.right);
       return t1;
    }
}

复杂度分析

时间复杂度 O(n)
空间复杂度 O(h),h为树的高度

解法2 迭代法

借助队列来实现广度优先遍历算法,只要两颗树的左节点都不为null,就把将他们放入队列中;同理只要两棵树的右节点都不为null了,也将他们放入队列中。然后我们不断的从队列中取出节点,把他们相加。

iterator.gif

图片来源:https://leetcode-cn.com/problems/merge-two-binary-trees/solution/dong-hua-yan-shi-di-gui-die-dai-617he-bing-er-cha-/

代码实现

class Solution {
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
       if (t1 == null || t2 == null) {
           return t1 == null ? t2 : t1;
       }
       LinkedList<TreeNode> queue = new LinkedList<>();
       queue.add(t1);
       queue.add(t2);
       while (queue.size() > 0) {
           TreeNode r1 = queue.remove();
           TreeNode r2 = queue.remove();
           r1.val += r2.val;
           if (r1.left != null && r2.left != null) {
               queue.add(r1.left);
               queue.add(r2.left);
           } else if (r1.left == null) {
               r1.left = r2.left;
           }

           if (r1.right != null && r2.right != null) {
               queue.add(r1.right);
               queue.add(r2.right);
           } else if (r1.right == null) {
               r1.right = r2.right;
           }
       }
       return t1;
    }
}

复杂度分析

时间复杂度 O(n)
空间复杂度 O(n)

在IntelliJ IDEA中测试

package com.cn.algorithm;

import java.util.LinkedList;

/**
 * @Author: cyh
 * @Description: 合并二叉树
 **/
public class MergeTreesDemo {
    public static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x) { val = x; }
    }

    TreeNode arrayToTree(Integer[] array, int index) {
        TreeNode root = null;
        if (index < array.length) {
            Integer value = array[index];
            if (value == null) {
                return null;
            }
            root = new TreeNode(value);
            root.left = arrayToTree(array, 2*index + 1);
            root.right = arrayToTree(array, 2*index + 2);
            return root;
        }
        return root;
    }

    /**
     * 递归法
     * @param t1
     * @param t2
     * @return
     */
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
        if (t1 == null || t2 == null) {
            return t1 == null ? t2 : t1;
        }
        t1.val += t2.val;
        t1.left = mergeTrees(t1.left, t2.left);
        t1.right = mergeTrees(t1.right, t2.right);
        return t1;
    }

    /**
     * 迭代法
     * @param t1
     * @param t2
     * @return
     */
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
        if (t1 == null || t2 == null) {
            return t1 == null ? t2 : t1;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.add(t1);
        queue.add(t2);
        while (queue.size() > 0) {
            TreeNode r1 = queue.remove();
            TreeNode r2 = queue.remove();
            r1.val += r2.val;
            if (r1.left != null && r2.left != null) {
                queue.add(r1.left);
                queue.add(r2.left);
            } else if (r1.left == null) {
                r1.left = r2.left;
            }

            if (r1.right != null && r2.right != null) {
                queue.add(r1.right);
                queue.add(r2.right);
            } else if (r1.right == null) {
                r1.right = r2.right;
            }
        }
        return t1;
    }

    public static void main(String[] args) {
        MergeTreesDemo demo = new MergeTreesDemo();
        Integer[] arr1 = {1, 3, 2, 5};
        Integer[] arr2 = {2, 1, 3, null, 4, null, 7};
        TreeNode root1 = demo.arrayToTree(arr1, 0);
        TreeNode root2 = demo.arrayToTree(arr2, 0);
        TreeNode root = demo.mergeTrees(root1, root2);
        System.out.println(root); //方便断点调试
    }
}

112 路径总和

题目描述

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

LeetCode

给定如下二叉树,以及目标和 sum = 22
                           5
             / \
            4   8
           /   / \
          11  13  4
         /  \      \
        7    2      1

解法1 递归法

遍历整棵树,如果当前节点不是叶子,对它的所有孩子节点,递归调用 hasPathSum 函数,其中 sum 值减去当前节点的权值;如果当前节点是叶子,检查 sum 值是否为 0,也就是是否找到了给定的目标和。

实现代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean hasPathSum(TreeNode root, int sum) {
        if (root == null) {
            return false;
        }
        sum = sum - root.val;
        if (root.left == null && root.right == null) {
            return (sum == 0);
        }
        return hasPathSum(root.left, sum) || hasPathSum(root.right, sum);
    }
}

复杂度分析

时间复杂度 O(n)
空间复杂度 最坏O(h),最好O(log(n)),h为树的高度

解法2 迭代法

使用深度优先遍历进行迭代,通过辅助栈来实现。

代码实现

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean hasPathSum(TreeNode root, int sum) {
        LinkedList<TreeNode> stackTree = new LinkedList<>();
        LinkedList<Integer> stackSum = new LinkedList<>();
        if (root == null) return false;
        stackTree.push(root);
        stackSum.push(sum - root.val);
        while (!stackTree.isEmpty()) {
           TreeNode node = stackTree.pollLast();
           Integer currSum = stackSum.pollLast();
           if ((node.right == null) && (node.left == null) && (currSum == 0)) {
               return true;
           }
           if (node.right != null) {
               stackTree.add(node.right);
                stackSum.add(currSum - node.right.val);
           }
           if (node.left != null) {
               stackTree.add(node.left);
               stackSum.add(currSum - node.left.val);
           }
        }
        return false;
    }
}

复杂度分析

时间复杂度 O(n)
空间复杂度 O(n)

参考:

CyC:https://cyc2018.github.io/CS-Notes

牛客网:https://www.nowcoder.com/questionTerminal

力扣:https://leetcode-cn.com/


评论
评论
  目录