第12节.pptx

二叉树的递归套路

1)假设以X节点为头,假设可以向X左树和X右树要任何信息
2)在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要)
3)列出所有可能性后,确定到底需要向左树和右树要什么样的信息
4)把左树信息和右树信息求全集,就是任何一棵子树都需要返回的信息S
5)递归函数都返回S,每一棵子树都这么要求
6)写代码,在代码中考虑如何把左树的信息和右树信息整合出整棵树的信息

题目1:是否为完全二叉树

完全二叉树:二叉树每一层节点都是满的,如有有节点不满的层,那一定是最后一层,且有从左往右变满的趋势

  1. // 判断二叉树是否是完全二叉树
  2. // 完全二叉树:树的每一层都是满树,就算不满,也是最后一层不满,也是从左往右变满的趋势
  3. public static boolean completeBT(TreeNode root) {
  4. if (root == null) {
  5. return true;
  6. }
  7. // 二叉树按层遍历
  8. LinkedList<TreeNode> queue = new LinkedList<>();
  9. boolean hasNoChild = false;
  10. queue.offer(root);
  11. while (!queue.isEmpty()) {
  12. TreeNode node = queue.poll();
  13. TreeNode leftChild = node.left;
  14. TreeNode rightChild = node.right;
  15. // 遇到hasNoChild的节点后,后面的节点必须是叶节点(没有2个孩子)
  16. if (hasNoChild && (leftChild != null || rightChild != null)) {
  17. return false;
  18. }
  19. // 没有左孩子,有有孩子,直接返回false
  20. if (rightChild != null && leftChild == null) {
  21. return false;
  22. }
  23. if (leftChild != null) {
  24. queue.offer(leftChild);
  25. }
  26. if (rightChild != null) {
  27. queue.offer(rightChild);
  28. }
  29. // 第一次遇到没有孩子的节点后,将hasNoChild 设为true
  30. if (leftChild == null || rightChild == null) {
  31. hasNoChild = true;
  32. }
  33. }
  34. return true;
  35. }
  1. public static boolean completeBT2(TreeNode root) {
  2. if (root == null) {
  3. return true;
  4. }
  5. return process(root).completeBT;
  6. }
  7. private static class Info {
  8. boolean fullTree;
  9. boolean completeBT;
  10. int heigth;
  11. public Info(boolean fullTree, boolean completeBT, int heigth) {
  12. this.fullTree = fullTree;
  13. this.completeBT = completeBT;
  14. this.heigth = heigth;
  15. }
  16. }
  17. private static Info process(TreeNode node) {
  18. if (node == null) {
  19. return new Info(true, true, 0);
  20. }
  21. Info leftInfo = process(node.left);
  22. Info rightInfo = process(node.right);
  23. //1 以node为根节点的二叉树是否为满二叉树:左子树为满二叉树,右子树为满二叉树,同时左子树与右子树一样高
  24. boolean fullTree = leftInfo.fullTree && rightInfo.fullTree && leftInfo.heigth == rightInfo.heigth;
  25. //2 以node为根节点的二叉树的高度,左子树和右子树最大高度+1
  26. int height = Math.max(leftInfo.heigth, rightInfo.heigth) + 1;
  27. //3 以node为根节点的二叉树是否为完全二叉树,先设为false
  28. boolean completeBT = false;
  29. //3.1 如果以node为根节点的二叉树是满二叉树,那么一定是完全二叉树
  30. if (fullTree) {
  31. completeBT = true;
  32. } else {
  33. boolean differ = (leftInfo.heigth - rightInfo.heigth == 1);
  34. //3.2 如果以node为根节点的二叉树不是满二叉树,但是做子树和右子树是满二叉树
  35. if (leftInfo.fullTree && rightInfo.fullTree) {
  36. //3.2.1 左子树、右子树都是满树,那么只有左子树比右子树多1层,以node为根节点的二叉树才是完全二叉树
  37. if (differ) {
  38. completeBT = true;
  39. }
  40. } else if (!leftInfo.fullTree && rightInfo.fullTree) {
  41. //3.2.2 左子树不是满树,右子树是满树,那么必须左子树是完全二叉树,左子树比右子树多1层
  42. if (leftInfo.completeBT && differ) {
  43. completeBT = true;
  44. }
  45. } else if (leftInfo.fullTree) {
  46. //3.2.3 左子树是满树,右子树不是满树,那么必须右子树必须是完全二叉树,左子树和右子树一样高
  47. if (rightInfo.completeBT && (leftInfo.heigth == rightInfo.heigth)) {
  48. completeBT = true;
  49. }
  50. }
  51. }
  52. return new Info(fullTree, completeBT, height);
  53. }

题目2:是否为搜索二叉树

搜索二叉树:搜索二叉树每一个节点值都不相同,且根节点的值大于左子树的最大值,小于右子树最小值

  1. public static boolean isSearchBT2(TreeNode root) {
  2. if (root == null) {
  3. return true;
  4. }
  5. ArrayList<TreeNode> arr = new ArrayList<>();
  6. in(root, arr);
  7. for (int i = 0; i < arr.size() - 1; i++) {
  8. if (arr.get(i).value >= arr.get(i + 1).value) {
  9. return false;
  10. }
  11. }
  12. return true;
  13. }
  14. private static void in(TreeNode node, ArrayList<TreeNode> arr) {
  15. if (node == null) {
  16. return;
  17. }
  18. in(node.left, arr);
  19. arr.add(node);
  20. in(node.right, arr);
  21. }
  1. //判断二叉树是否是搜索二叉树
  2. // 搜索二叉树,左子树最大值小于头结点,右子树最小值大于头结点的值
  3. public static boolean isSearchBT(TreeNode root) {
  4. if (root == null) {
  5. return true;
  6. }
  7. return process(root).searchTree;
  8. }
  9. private static class Info {
  10. boolean searchTree;
  11. int maxValue;
  12. int minValue;
  13. public Info(boolean searchTree, int maxValue, int minValue) {
  14. this.searchTree = searchTree;
  15. this.maxValue = maxValue;
  16. this.minValue = minValue;
  17. }
  18. }
  19. private static Info process(TreeNode node) {
  20. if (node == null) {
  21. return null;
  22. }
  23. Info leftInfo = process(node.left);
  24. Info rightInfo = process(node.right);
  25. int maxValue = node.value;
  26. int minValue = node.value;
  27. if (leftInfo != null) {
  28. maxValue = Math.max(maxValue, leftInfo.maxValue);
  29. minValue = Math.min(minValue, leftInfo.minValue);
  30. }
  31. if (rightInfo != null) {
  32. maxValue = Math.max(maxValue, rightInfo.maxValue);
  33. minValue = Math.min(minValue, rightInfo.minValue);
  34. }
  35. boolean searchTree = false;
  36. if (leftInfo != null && rightInfo != null) {
  37. searchTree = leftInfo.searchTree && rightInfo.searchTree && (leftInfo.maxValue < node.value) && (rightInfo.minValue > node.value);
  38. } else if (rightInfo != null) {
  39. searchTree = rightInfo.searchTree && (rightInfo.minValue > node.value);
  40. } else if (leftInfo != null) {
  41. searchTree = leftInfo.searchTree && (leftInfo.maxValue < node.value);
  42. } else {
  43. searchTree = true;
  44. }
  45. return new Info(searchTree, maxValue, minValue);
  46. }

题目3:是否为平衡二叉树

平衡二叉树:任一节点对应的两棵子树的最大高度差为1,因此它也被称为平衡二叉树

  1. //是否为平衡二叉树
  2. // 平衡二叉树,任一节点对应的两棵子树的最大高度差为1,因此它也被称为平衡二叉树
  3. public static boolean isBalancedTree(TreeNode root) {
  4. if (root == null) {
  5. return true;
  6. }
  7. return process(root).balancedTree;
  8. }
  9. private static class Info {
  10. boolean balancedTree;
  11. int height;
  12. public Info(boolean balancedTree, int height) {
  13. this.balancedTree = balancedTree;
  14. this.height = height;
  15. }
  16. }
  17. private static Info process(TreeNode node) {
  18. if (node == null) {
  19. return new Info(true, 0);
  20. }
  21. Info leftInfo = process(node.left);
  22. Info rightInfo = process(node.right);
  23. int height = Math.max(leftInfo.height, rightInfo.height) + 1;
  24. boolean balancedTree = leftInfo.balancedTree && rightInfo.balancedTree && (Math.abs(leftInfo.height - rightInfo.height) < 2);
  25. return new Info(balancedTree, height);
  26. }
public static boolean isBalancedTree2(TreeNode root) {
    if (root == null) {
        return true;
    }
    boolean[] ans = {true};
    process2(root, ans);
    return ans[0];
}

private static int process2(TreeNode node, boolean[] ans) {
    if (!ans[0] || node == null) {
        return -1;
    }
    int leftHeight = process2(node.left, ans);
    int rightHeight = process2(node.right, ans);
    if (Math.abs(leftHeight - rightHeight) > 1) {
        ans[0] = false;
    }
    return Math.max(leftHeight, rightHeight) + 1;
}

题目4:是否为满二叉树

满二叉树:每一层节点都是满

// 第1种方法
// 收集子树是否是满二叉树
// 收集子树的高度
// 左树满 && 右树满 && 左右树高度一样 -> 整棵树是满的

public static boolean isFullTree(TreeNode root) {
    if (root == null) {
        return true;
    }
    return precess(root).full;
}

private static class Info {
    int height;
    boolean full;

    public Info(int height, boolean full) {
        this.height = height;
        this.full = full;
    }
}

private static Info precess(TreeNode node) {
    if (node == null) {
        return new Info(0, true);
    }
    Info leftInfo = precess(node.left);
    Info rightInfo = precess(node.right);

    int height = Math.max(leftInfo.height, rightInfo.height) + 1;
    boolean full = leftInfo.full && rightInfo.full && leftInfo.height == rightInfo.height;
    return new Info(height, full);
}

public static boolean isFullTree2(TreeNode root) {
    if (root == null) {
        return true;
    }
    Info2 info2 = process2(root);
    return info2.nodes == (1 << info2.height) - 1;

}
// 第2种方法
// 收集整棵树的高度h,和节点数n
// 只有满二叉树满足 : 2 ^ h - 1 == n

private static class Info2 {
    int height;
    int nodes;

    public Info2(int height, int nodes) {
        this.height = height;
        this.nodes = nodes;
    }
}

private static Info2 process2(TreeNode node) {
    if (node == null) {
        return new Info2(0, 0);
    }
    Info2 leftInfo = process2(node.left);
    Info2 rightInfo = process2(node.right);
    int nodes = leftInfo.nodes + rightInfo.nodes + 1;
    int height = Math.max(leftInfo.height, rightInfo.height) + 1;
    return new Info2(height, nodes);
}

题目5:返回二叉树最大的二叉搜索子树的大小

// 给定一棵二叉树的头节点head,
//返回这颗二叉树中最大的二叉搜索子树的大小

public static int maxSBTSize(TreeNode root) {
    if (root == null) {
        return 0;
    }
    return precess(root).maxSBTSize;
}

private static class Info {
    int maxSBTSize;
    int size;
    int max;
    int min;

    public Info(int maxSBTSize, int size, int max, int min) {
        this.maxSBTSize = maxSBTSize;
        this.size = size;
        this.max = max;
        this.min = min;
    }
}

private static Info precess(TreeNode node) {
    if (node == null) {
        return null;
    }

    Info leftInfo = precess(node.left);
    Info rightInfo = precess(node.right);

    int size = 1;
    int max = node.value;
    int min = node.value;

    if (leftInfo != null) {
        size += leftInfo.size;
        max = Math.max(max, leftInfo.max);
        min = Math.min(min, leftInfo.min);
    }
    if (rightInfo != null) {
        size += rightInfo.size;
        max = Math.max(max, rightInfo.max);
        min = Math.min(min, rightInfo.min);
    }

    int p1 = leftInfo != null ? leftInfo.maxSBTSize : 0;
    int p2 = rightInfo != null ? rightInfo.maxSBTSize : 0;
    int p3 = 0;
    boolean leftIsSearchTree = leftInfo == null || leftInfo.maxSBTSize == leftInfo.size;
    boolean rightSearchTree = rightInfo == null || rightInfo.maxSBTSize == rightInfo.size;

    if (leftIsSearchTree && rightSearchTree) {
        boolean leftMaxLessNode = leftInfo == null || leftInfo.max < node.value;
        boolean rightMinMoreNode = rightInfo == null || rightInfo.min > node.value;
        if (leftMaxLessNode && rightMinMoreNode) {
            int leftSize = leftInfo == null ? 0 : leftInfo.size;
            int rightSize = rightInfo == null ? 0 : rightInfo.size;
            p3 = leftSize + rightSize + 1;
        }
    }
    int maxSBTSize = Math.max(p1, Math.max(p2, p3));
    return new Info(maxSBTSize, size, max, min);
}
public static int maxSBTSize2(TreeNode root) {
    if (root == null) {
        return 0;
    }
    int bstSize = getBSTSize(root);
    if (bstSize != 0) {
        return bstSize;
    }
    return Math.max(maxSBTSize2(root.left), maxSBTSize2(root.right));
}

private static int getBSTSize(TreeNode root) {
    if (root == null) {
        return 0;
    }
    ArrayList<TreeNode> arr = new ArrayList<>();
    in(root, arr);
    for (int i = 0; i < arr.size() - 1; i++) {
        if (arr.get(i).value >= arr.get(i + 1).value) {
            return 0;
        }
    }
    return arr.size();
}

private static void in(TreeNode node, ArrayList<TreeNode> arr) {
    if (node == null) {
        return;
    }
    in(node.left, arr);
    arr.add(node);
    in(node.right, arr);
}

题目6:返回整棵二叉树的最大距离

给定一棵二叉树的头节点head,任何两个节点之间都存在距离,返回整棵二叉树的最大距离

// 给定一棵二叉树的头节点head,任何两个节点之间都存在距离,返回整棵二叉树的最大距离

public static int maxDistance(TreeNode root) {
    if (root == null) {
        return 0;
    }
    return process(root).maxDistance;
}


private static class Info {
    int height;
    int maxDistance;

    public Info(int height, int maxDistance) {
        this.height = height;
        this.maxDistance = maxDistance;
    }
}

private static Info process(TreeNode node) {
    if (node == null) {
        return new Info(0, 0);
    }
    Info leftInfo = process(node.left);
    Info rightInfo = process(node.right);

    int height = Math.max(leftInfo.height, rightInfo.height) + 1;
    int maxDistance = Math.max(Math.max(leftInfo.maxDistance, rightInfo.maxDistance), leftInfo.height + rightInfo.height + 1);
    return new Info(height, maxDistance);
}
public static int maxDistance2(TreeNode head) {
    if (head == null) {
        return 0;
    }
    ArrayList<TreeNode> arr = getPrelist(head);
    HashMap<TreeNode, TreeNode> parentMap = getParentMap(head);
    int max = 0;
    for (int i = 0; i < arr.size(); i++) {
        for (int j = i; j < arr.size(); j++) {
            max = Math.max(max, distance(parentMap, arr.get(i), arr.get(j)));
        }
    }
    return max;
}

public static ArrayList<TreeNode> getPrelist(TreeNode head) {
    ArrayList<TreeNode> arr = new ArrayList<>();
    fillPrelist(head, arr);
    return arr;
}

public static void fillPrelist(TreeNode head, ArrayList<TreeNode> arr) {
    if (head == null) {
        return;
    }
    arr.add(head);
    fillPrelist(head.left, arr);
    fillPrelist(head.right, arr);
}

public static HashMap<TreeNode, TreeNode> getParentMap(TreeNode head) {
    HashMap<TreeNode, TreeNode> map = new HashMap<>();
    map.put(head, null);
    fillParentMap(head, map);
    return map;
}

public static void fillParentMap(TreeNode head, HashMap<TreeNode, TreeNode> parentMap) {
    if (head.left != null) {
        parentMap.put(head.left, head);
        fillParentMap(head.left, parentMap);
    }
    if (head.right != null) {
        parentMap.put(head.right, head);
        fillParentMap(head.right, parentMap);
    }
}

public static int distance(HashMap<TreeNode, TreeNode> parentMap, TreeNode o1, TreeNode o2) {
    HashSet<TreeNode> o1Set = new HashSet<>();
    TreeNode cur = o1;
    o1Set.add(cur);
    while (parentMap.get(cur) != null) {
        cur = parentMap.get(cur);
        o1Set.add(cur);
    }
    cur = o2;
    while (!o1Set.contains(cur)) {
        cur = parentMap.get(cur);
    }
    TreeNode lowestAncestor = cur;
    cur = o1;
    int distance1 = 1;
    while (cur != lowestAncestor) {
        cur = parentMap.get(cur);
        distance1++;
    }
    cur = o2;
    int distance2 = 1;
    while (cur != lowestAncestor) {
        cur = parentMap.get(cur);
        distance2++;
    }
    return distance1 + distance2 - 1;
}

题目7:返回这颗二叉树中最大的二叉搜索子树的头节点

给定一棵二叉树的头节点head,返回这颗二叉树中最大的二叉搜索子树的头节点

//给定一棵二叉树的头节点head,
//返回这颗二叉树中最大的二叉搜索子树的头节点

public static TreeNode maxSubSearchTreeHead(TreeNode root) {
    if (root == null) {
        return null;
    }
    return process(root).head;
}

private static class Info {
    int subSearchTreeSize;
    int max;
    int min;
    TreeNode head;

    public Info(int subSearchTreeSize, int max, int min, TreeNode head) {
        this.subSearchTreeSize = subSearchTreeSize;
        this.max = max;
        this.min = min;
        this.head = head;
    }
}

private static Info process(TreeNode node) {
    if (node == null) {
        return null;
    }

    Info leftInfo = process(node.left);
    Info rightInfo = process(node.right);

    int subSearchTreeSize = 0;
    int max = node.value;
    int min = node.value;
    TreeNode head = null;

    if (leftInfo != null) {
        max = Math.max(leftInfo.max, max);
        min = Math.min(leftInfo.min, min);
        subSearchTreeSize = leftInfo.subSearchTreeSize;
        head = leftInfo.head;
    }
    if (rightInfo != null) {
        max = Math.max(rightInfo.max, max);
        min = Math.min(rightInfo.min, min);
        if (rightInfo.subSearchTreeSize > subSearchTreeSize) {
            subSearchTreeSize = rightInfo.subSearchTreeSize;
            head = rightInfo.head;
        }
    }
    // 左子树为空或者左子树为搜索树,且左子树最大值小于头结点值
    boolean leftBool = leftInfo == null || ((leftInfo.head == node.left) && (leftInfo.max < node.value));
    // 右子树为空或者右子树为搜索树,且右子树最小值大于头结点值
    boolean rightBool = rightInfo == null || ((rightInfo.head == node.right) && (rightInfo.min > node.value));
    // 左子树、右子树满足条件
    if (leftBool && rightBool) {
        head = node;
        subSearchTreeSize = (leftInfo == null ? 0 : leftInfo.subSearchTreeSize) + (rightInfo == null ? 0 : rightInfo.subSearchTreeSize) + 1;
    }
    return new Info(subSearchTreeSize, max, min, head);
}
public static TreeNode maxSubSearchTreeHead2(TreeNode head) {
    if (head == null) {
        return null;
    }
    int bstSize = getBSTSize(head);
    // 不为0,则head为头结点的二叉树为搜索二叉树
    if (bstSize != 0) {
        return head;
    }
    // 左子树返回的头结点
    TreeNode left = maxSubSearchTreeHead2(head.left);
    // 右子树返回的头结点
    TreeNode right = maxSubSearchTreeHead2(head.right);
    // 返回左、右子树头结点子树size大的子树头结点
    return getBSTSize(left) >= getBSTSize(right) ? left : right;


}
// 获取以node为头结点的搜索树的大小 node为空或不是搜索树返回0
private static int getBSTSize(TreeNode node) {
    if (node == null) {
        return 0;
    }
    ArrayList<TreeNode> arr = new ArrayList<>();
    in(node, arr);
    for (int i = 0; i < arr.size()-1; i++) {
        if (arr.get(i).value >= arr.get(i + 1).value) {
            return 0;
        }
    }
    return arr.size();
}
// 中序遍历放入ArrayList<TreeNode>集合中
private static void in(TreeNode node, ArrayList<TreeNode> arr) {
    if (node == null) {
        return;
    }
    in(node.left, arr);
    arr.add(node);
    in(node.right, arr);
}

题目8:返回两个节点最低公共祖先

给定一棵二叉树的头节点head,和另外两个节点a和b。返回a和b的最低公共祖先

//给定一棵二叉树的头节点head,和另外两个节点a和b。
//返回a和b的最低公共祖先
public static TreeNode lowestAncestor(TreeNode head, TreeNode a, TreeNode b) {
    if (head == null) {
        return null;
    }

    return process(head, a, b).lowestAncestor;
}

private static class Info {
    boolean findA;
    boolean findB;
    TreeNode lowestAncestor;

    public Info(boolean findA, boolean findB, TreeNode lowestAncestor) {
        this.findA = findA;
        this.findB = findB;
        this.lowestAncestor = lowestAncestor;
    }
}

private static Info process(TreeNode node, TreeNode a, TreeNode b) {
    if (node == null) {
        return new Info(false, false, null);
    }

    Info leftInfo = process(node.left, a, b);
    Info rightInfo = process(node.right, a, b);

    boolean findA = leftInfo.findA || rightInfo.findA || node == a;
    boolean findB = leftInfo.findB || rightInfo.findB || node == b;
    TreeNode lowestAncestor = null;

    if (leftInfo.lowestAncestor != null) {
        lowestAncestor = leftInfo.lowestAncestor;
    } else if (rightInfo.lowestAncestor != null) {
        lowestAncestor = rightInfo.lowestAncestor;
    } else if (findA && findB) {
        lowestAncestor = node;
    }
    return new Info(findA, findB, lowestAncestor);
}
public static TreeNode lowestAncestor2(TreeNode head, TreeNode a, TreeNode b) {
    if (head == null) {
        return null;
    }
    Map<TreeNode, TreeNode> parentMap = new HashMap<>();
    parentMap.put(head, null);
    fillParentMap(head, parentMap);
    Set<TreeNode> set1 = new HashSet<>();
    TreeNode cur = a;
    set1.add(cur);
    while (parentMap.get(cur) != null) {
        cur = parentMap.get(cur);
        set1.add(cur);
    }
    cur = b;
    while (!set1.contains(cur)) {
        cur = parentMap.get(cur);
    }
    return cur;
}

public static void fillParentMap(TreeNode head, Map<TreeNode, TreeNode> parentMap) {
    if (head.left != null) {
        parentMap.put(head.left, head);
        fillParentMap(head.left, parentMap);
    }
    if (head.right != null) {
        parentMap.put(head.right, head);
        fillParentMap(head.right, parentMap);
    }
}

题目9:派对的最大快乐值

公司的每个员工都符合 Employee 类的描述。整个公司的人员结构可以看作是一棵标准的、 没有环的多叉树。树的头节点是公司唯一的老板。除老板之外的每个员工都有唯一的直接上级。 叶节点是没有任何下属的基层员工(subordinates列表为空),除基层员工外,每个员工都有一个或多个直接下级。
这个公司现在要办party,你可以决定哪些员工来,哪些员工不来,规则:
1.如果某个员工来了,那么这个员工的所有直接下级都不能来
2.派对的整体快乐值是所有到场员工快乐值的累加
3.你的目标是让派对的整体快乐值尽量大
给定一棵多叉树的头节点boss,请返回派对的最大快乐值。

private static class Employee {
    int happy;
    List<Employee> subordinates;

    public Employee(int happy) {
        this.happy = happy;
        subordinates = new ArrayList<>();
    }
}

public static int maxHappy(Employee boss) {
    if (boss == null) {
        return 0;
    }
    Info bossInfo = process(boss);
    return Math.max(bossInfo.noAttendMaxHappy, bossInfo.attendMaxHappy);
}

private static class Info {
    int attendMaxHappy;
    int noAttendMaxHappy;

    public Info(int attendMaxHappy, int noAttendMaxHappy) {
        this.attendMaxHappy = attendMaxHappy;
        this.noAttendMaxHappy = noAttendMaxHappy;
    }
}

private static Info process(Employee employee) {
    if (employee == null) {
        return new Info(0, 0);
    }
    int attendMaxHappy = employee.happy;
    int noAttendMaxHappy = 0;

    for (Employee subordinate : employee.subordinates) {
        Info info = process(subordinate);
        attendMaxHappy += info.noAttendMaxHappy;
        noAttendMaxHappy += Math.max(info.attendMaxHappy, info.noAttendMaxHappy);
    }
    return new Info(attendMaxHappy, noAttendMaxHappy);
}
public static int maxHappy2(Employee boss) {
    if (boss == null) {
        return 0;
    }
    return process2(boss, false);
}
// 当前员工和上级是否参加
private static int process2(Employee cur, boolean leaderAttend) {
    // 上级参加,那么当前员工不参加
    if (leaderAttend) {
        int ans = 0;
        for (Employee c : cur.subordinates) {
            ans += process2(c, false);
        }
        return ans;
    } else {
        // 上级不参加,当前员工可参加,也可不参加
        int p1 = cur.happy;
        int p2 = 0;
        for (Employee c : cur.subordinates) {
            p1 += process2(c, true);
            p2 += process2(c, false);
        }
        return Math.max(p1, p2);
    }
}