函数加工

给定一个函数 f,可以 1~5 的数字等概率返回一个。请加工出 1~7 的数字等概率返回一个的函数 g。

思路:先将函数 f 转化为能随机得到 0、1的函数 r01 ,然后将 r01 没得到一个数就放到一个二进制位上,得到想要的范围内的数,如果超出范围就重新随机

代码实现:

  1. public class Rand5ToRand7 {
  2. /**
  3. * 1 ~ 5
  4. */
  5. public static int f() {
  6. return (int) (Math.random() * 5 + 1);
  7. }
  8. /**
  9. * 0 1
  10. */
  11. public static int r01() {
  12. int num = 0;
  13. // 如果等于 3 就一直循环
  14. do {
  15. num = f();
  16. } while (num == 3);
  17. return num > 3 ? 0 : 1;
  18. }
  19. /**
  20. * 1 ~ 7
  21. */
  22. public static int g() {
  23. // 1 ~ 7 = (0 ~ 6) + 1
  24. // 6 是使用 3 个二进制位就可以表示,如果大于 6 就重新随机
  25. int num = 0;
  26. do {
  27. num = (r01() << 2) + (r01() << 1) + r01();
  28. } while (num > 6);
  29. return num + 1;
  30. }
  31. public static void main(String[] args) {
  32. for (int i = 10; i > 0; i--) {
  33. System.out.println(g());
  34. }
  35. }
  36. }

给定一个函数 f,可以 a~b 的数字等概率返回一个。请加工出 c~d 的数字等概率返回一个的函数 g。
同样的思路:

  1. 将 f 加工为 0、1 发生器 r01,具体步骤:
    1. 计算 f 范围的中间值,mid = (b+a)/2
    2. 如果 b+a 是偶数,f() < mid 为 1,f() > mid 为 0,f() == mid 重新执行;如果 b+a 是奇数,f() <= mid 为 1,f() > mid 为 0
  2. r01 加工为 0 ~ (d-c) 范围,用 r01 代表二进制的每一位,超出范围就重新执行
  3. 返回结果 + c 就是在 c~d 范围

给定一个函数 f,以 p 概率返回 0,以 1-p 概率返回 1。请加工出等概率返回 0 和 1 的函数 g。
思路:
让函数 f 执行两次,如果结果为 00 或者 11 就重新执行两次;如果结果是 01 返回 0;结果是 10 返回 1。
f 执行两次的结果概率为:

  • 00 : p*p
  • 11 : (1-p) * (1-p)
  • 01 : p * (1-p)
  • 10 : (1-p) * p

01 和 10 的概率相同

n 个二叉树

给定一个非负整数 n,代表二叉树的节点个数。返回能形成多少种不同的二叉树结构

思路:
对于 n 个节点来说,存在如下情况:

  • n = 0,一种情况空节点
  • n = 1,一种情况单节点
  • n = 2,两种情况,头节点+左子节点、头节点+右子节点
  • n = i,……

image.png

代码实现:

  1. public class UniqueBST {
  2. /**
  3. * 递归版本
  4. */
  5. public static int numTrees(int n) {
  6. if (n < 0) return 0;
  7. if (n == 0) return 1;
  8. if (n == 1) return 1;
  9. if (n == 2) return 2;
  10. int res = 0;
  11. for (int leftNum = 0; leftNum <= n - 1; leftNum++) {
  12. int leftWays = numTrees(leftNum);
  13. int rightWays = numTrees(n - 1 - leftNum);
  14. res += leftWays * rightWays;
  15. }
  16. return res;
  17. }
  18. /**
  19. * 动态规划
  20. */
  21. public static int numTrees1(int n) {
  22. if (n < 2) {
  23. return 1;
  24. }
  25. int[] num = new int[n + 1];
  26. num[0] = 1;
  27. for (int i = 1; i < n + 1; i++) {
  28. for (int j = 0; j <= i - 1; j++) {
  29. num[i] += num[j] * num[i - j - 1];
  30. }
  31. }
  32. return num[n];
  33. }
  34. public static void main(String[] args) {
  35. for (int i = 0; i < 15; i++) {
  36. System.out.println(numTrees(i));
  37. System.out.println(numTrees1(i));
  38. }
  39. }
  40. }

完整的括号

一个完整的括号字符串定义规则如下:

  1. 空字符串是完整的。
  2. 如果 s 是完整的字符串,那么 (s) 也是完整的。
  3. 如果 st 是完整的字符串,将它们连接起来形成的 st 也是完整的。

例如,"(()())""""(())()" 是完整的括号字符串,"())(""()("")" 是不完整的括号字符串。
牛牛有一个括号字符串 s,现在需要在其中任意位置尽量少地添加括号,将其转化为一个完整的括号字符串。请问牛牛至少需要添加多少个括号?

思路一:
如果将能匹配的括号对“消除掉”后,剩下的就是不能匹配的括号,有多少这样不能匹配的括号就会需要添加多少括号。具体步骤:

  1. 新建一个栈
  2. 将字符串按顺序遍历,当前字符为 cur,判断当前的 cur 是否组成了括号对,是否需要被“消除”
    • 如果栈为空,将 cur 放入栈中
    • 如果栈不为空,且 cur 为左括号,那就取栈顶元素是否为右括号,如果是右括号说明能组成括号对,将栈顶弹出,相当于执行了“消除”
    • 如果栈不为空,且 cur 为右括号,说明不能组成括号对,直接放入栈中
  3. 此时,在栈中就是不能组成括号对的数量,即:需要添加多少括号

思路二:
思路一的优点是只需要遍历一次字符串就能得到结果,但是在需要使用容器来存放不能匹配的括号,那么能不能优化栈使用的空间呢?可以使用统计括号的思路:
首先,如果判断一个字符串是否是完整的括号字符串,就可以这样统计:

  1. 一个统计值 count
  2. 遍历字符串,如果遇到左括号就 ++,遇到右括号就 —
  3. 如果 count 出现负数的情况,就说明出现了 )( 这样的括号。也就是说出现了不能匹配的括号,遍历结束
  4. 如果遍历完成 count == 0,且中间未出现 count < 0 的情况,说明了该字符串是完整括号字符串

伪代码如下:

  1. boolean isParentheses(String str) {
  2. int count = 0;
  3. for (int i = 0; i < str.length(); i++) {
  4. char c = str.charAt(i);
  5. if (c == '(') {
  6. count ++;
  7. } else {
  8. count --;
  9. if (count < 0) {
  10. return false;
  11. }
  12. }
  13. }
  14. return count == 0;
  15. }

上面的逻辑就解决是否是完整的括号字符串的问题,适当改造下就可以用来解决需要添加多少个括号的问题,现在逻辑上找到不能匹配的情况:

  • count < 0 时,说明出现了不能匹配的情况
  • 最后遍历完成 count != 0 时,说明有不能匹配的情况

count 统计了左括号被“消除”后多出来的数量,needNum 统计了右括号多出来的数量,最后的结果就是 count + needNum

做如下改造:

  1. int needParentheses(String str) {
  2. int needNum = 0;
  3. int count = 0;
  4. for (int i = 0; i < str.length(); i++) {
  5. char c = str.charAt(i);
  6. if (c == '(') {
  7. count ++;
  8. } else {
  9. count --;
  10. if (count == -1) {
  11. count = 0;
  12. needNum ++;
  13. }
  14. }
  15. }
  16. return count + needNum;
  17. }

思路二其实使用两个变量来代替了原先的“消除”步骤

最后的代码实现:

  1. import java.util.Stack;
  2. public class NeedParentheses {
  3. // 思路一
  4. public static int needParentheses(String str) {
  5. Stack<Character> stack = new Stack<>();
  6. for (int i = 0; i < str.length(); i++) {
  7. char c = str.charAt(i);
  8. if (!stack.isEmpty()) {
  9. if (c == ')' && stack.peek() == '(') {
  10. stack.pop();
  11. } else {
  12. stack.push(c);
  13. }
  14. } else {
  15. stack.push(c);
  16. }
  17. }
  18. return stack.size();
  19. }
  20. // 思路二
  21. public static int needParentheses1(String str) {
  22. int leftRest = 0;
  23. int needSolveRight = 0;
  24. for (int i = 0; i < str.length(); i++) {
  25. if (str.charAt(i) == '(') {
  26. leftRest++;
  27. } else {
  28. if (leftRest == 0) {
  29. needSolveRight++;
  30. } else {
  31. leftRest--;
  32. }
  33. }
  34. }
  35. return leftRest + needSolveRight;
  36. }
  37. public static void main(String[] args) {
  38. System.out.println(needParentheses("())("));
  39. System.out.println(needParentheses1("())("));
  40. System.out.println(needParentheses("()("));
  41. System.out.println(needParentheses1("()("));
  42. System.out.println(needParentheses("("));
  43. System.out.println(needParentheses1("("));
  44. System.out.println(needParentheses("(((())((("));
  45. System.out.println(needParentheses1("(((())((("));
  46. }
  47. }

去重数字对

给定一个数组arr,求差值为 k 的去重数字对。

例如:
数组为 [3,2,5,7,0,0],k 为 2,那么去重的数字对就为:3,55,70,2
因为是去重的,所以即使有两个 0 也不能出现两次 0,2;也不能出现 3,5 5,3 这样的顺序不同的重复

思路:使用哈希表记录数组的值,然后遍历数组,判断该值 +k 后的值是否在哈希表中是否存在,如果存在就说明这两个数组成了一对

代码实现:

  1. package com.leetcodetest.middle;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.List;
  5. import java.util.Map;
  6. public class SubValueEqualK {
  7. public static List<List<Integer>> allPair(int[] arr, int k) {
  8. Map<Integer, Integer> map = new HashMap<>();
  9. for (int i : arr) {
  10. map.put(i, i + 2);
  11. }
  12. List<List<Integer>> list = new ArrayList<>();
  13. for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
  14. if (map.containsKey(entry.getValue())) {
  15. List<Integer> pair = new ArrayList<>();
  16. pair.add(entry.getKey());
  17. pair.add(entry.getValue());
  18. list.add(pair);
  19. }
  20. }
  21. return list;
  22. }
  23. public static void main(String[] args) {
  24. int[] arr = {3, 2, 5, 7, 0, 0};
  25. System.out.println(allPair(arr, 2));
  26. }
  27. }

magic 操作

给一个包含 n 个整数元素的集合 a,一个包含 m 个整数元素的集合 b。 定义 magic 操作为,从一个集合中取出一个元素,放到另一个集合里,且操作过后每个集合的平均值都大于操作前。 注意以下两点:

  1. 不可以把一个集合的元素取空,这样就没有平均值了
  2. 值为 x 的元素从集合 b 取出放入集合 a,但集合 a 中已经有值为 x 的元素,则a的平均值不变(因为集合元素不会重复),b的平均值可能会改变(因为 x 被取出了)

问最多可以进行多少次 magic 操作?

思路:
假设:

  • a 的平均值等于 b 的平均值,平均值都为 100:
    • 如果从 a 取出大于 100 的数,a 的平均值会减少,不成立
    • 如果从 a 取出等于 100 的数,a 的平均值不变,不成立
    • 如果从 a 取出小于 100 的数,a 的平均值变大,b 的平均值变小,不成立

所以,本情况不成立

  • a 的平均值 50 小于 b 的平均值 100:
    • 如果从 a 取出大于 50 的数,a 的平均值会减少,不成立
    • 如果从 a 取出等于 50 的数,a 的平均值不变,b 的平均值变小,不成立
    • 如果从 a 取出小于 50 的数,b 的平均值会减少,不成立
  • a 的平均值 100 大于 b 的平均值 50:
    • 如果从 a 取出大于 100 的数,a 的平均值会减少,不成立
    • 如果从 a 取出等于 100的数,a 的平均值不变,b 的平均值变大,不成立
    • 如果从 a 取出小于 100 的数,a 的平均值变大
      • 若该数大于 50,b 的平均值变大,成立
      • 若该数小于 50,b 的平均值变小,不成立

综上,确立了条件:只有当 a 的平均值 大于 b 的平均值,取出 x 小于 a 的平均值且大于 b 的平均值时,才能满足条件。

那么应该怎么取 x 呢?
假设,a = {60, 70, 80, 90, 100, 110, 120, 130, 140} 平均值为 100,b = {50} 平均值为 50,此时,满足 x 小于 a 的平均值且大于 b 的平均值 的值有 {60, 70, 80, 90}。

  • 从小到大取 60,取出 60 后,对 a 的平均值提升最大,变成 105;对 b 的平均值提升最小,变成 55; x 的取值范围,从 [50, 100] 变成 [55, 105]
  • 从大到小取 90,取出 90 后,对 a 的平均值提升最小,变成 101;对 b 的平均值提升最大,变成 70; x 的取值范围,从 [50, 100] 变成 [70, 101],变小了

综上,取 x 应该从小到大取

最多可以取多少次 magic 呢?
由于 x 的取值范围会发生变化,在具体到多少次的计算中,应该每次取完数后重新去下一个数,直到取出最后一个数为止。

代码实现:

  1. import java.util.ArrayList;
  2. import java.util.Arrays;
  3. import java.util.List;
  4. public class MagicOp {
  5. // 请保证arr1无重复值、arr2中无重复值,且arr1和arr2肯定有数字
  6. public static int maxOps(int[] arr1, int[] arr2) {
  7. // 排序
  8. Arrays.sort(arr1);
  9. Arrays.sort(arr2);
  10. // 和
  11. int sum1 = Arrays.stream(arr1).sum();
  12. int sum2 = Arrays.stream(arr2).sum();
  13. // 平均值
  14. double avg1 = sum1 / arr1.length;
  15. double avg2 = sum2 / arr2.length;
  16. if (avg2 >= avg1) return 0;
  17. List<Integer> optList = new ArrayList<>();
  18. Integer min = null;
  19. while (avg1 > avg2) {
  20. // 找到 > avg2 && < avg1 的最小的值
  21. for (int item : arr1) {
  22. if (item > avg2 && item < avg1 && !optList.contains(item)) {
  23. min = item;
  24. break;
  25. }
  26. }
  27. if (min == null || arr1.length == optList.size() + 1) {
  28. // 获取不到满足条件的值 或 已经要把 arr1 取完了 就停止
  29. break;
  30. }
  31. optList.add(min);
  32. // 重算平均值
  33. sum1 -= min;
  34. sum2 += min;
  35. avg1 = sum1 / (arr1.length - optList.size());
  36. avg2 = sum2 / (arr2.length + optList.size());
  37. System.out.println("取出 " + min + " 新的范围是 [" + avg2 + ", " + avg1 + "]");
  38. }
  39. return optList.size();
  40. }
  41. public static void main(String[] args) {
  42. System.out.println(maxOps(new int[]{60, 70, 80, 90, 100, 110, 120, 130, 140}, new int[]{50}));
  43. }
  44. }

数字转字符串

将给定的数转换为字符串,原则如下:1 对应 a,2 对应 b,…..26 对应 z,例如 12258
可以转换为 “abbeh”、”aveh”、”abyh”、”lbeh” 和 “lyh”,个数为 5,编写一个函数,给出可以转换的不同字符串的个数。

思路:
遍历字符串,从 i 位置开始:

  • i 不等于 0,直接转为字母,
  • i 等于 0 不能单独转为字母,必须和前面的 i-1 组合
  • i 和 i + 1 组成的数字转为字母,不能大于 26

开始递归

  1. public class NumToStringWays {
  2. // 递归
  3. public static int convertWays1(int num) {
  4. if (num <= 0) return 0;
  5. return process(num + "", 0);
  6. }
  7. public static int process(String str, int index) {
  8. if (index == str.length()) return 1;
  9. if (str.charAt(index) == '0') { // 不能以 0 开头,只能和之前的数组合
  10. return 0;
  11. }
  12. int res = 0;
  13. res += process(str, index + 1);
  14. if (str.length() >= index + 2) {
  15. if (Integer.parseInt(str.substring(index, index + 2)) <= 26) {
  16. res += process(str, index + 2);
  17. }
  18. }
  19. return res;
  20. }
  21. // 动态规划
  22. public static int convertWays2(int num) {
  23. if (num <= 0) return 0;
  24. String str = num + "";
  25. int length = str.length();
  26. int[] dp = new int[length + 1];
  27. dp[length] = 1;
  28. dp[length - 1] = str.charAt(length - 1) == '0' ? 0 : 1;
  29. for (int i = length - 2; i >= 0; i--) {
  30. if (str.charAt(i) == '0') {
  31. dp[i] = 0;
  32. } else {
  33. dp[i] = dp[i + 1];
  34. if (Integer.parseInt(str.substring(i, i + 2)) <= 26)
  35. dp[i] += dp[i + 2];
  36. }
  37. }
  38. return dp[0];
  39. }
  40. public static void main(String[] args) {
  41. System.out.println(convertWays1(12258));
  42. System.out.println(convertWays1(12));
  43. System.out.println(convertWays1(122));
  44. System.out.println(convertWays1(1207));
  45. System.out.println("=================");
  46. System.out.println(convertWays2(12258));
  47. System.out.println(convertWays2(12));
  48. System.out.println(convertWays2(122));
  49. System.out.println(convertWays2(1207));
  50. }
  51. }

括号的深度

一个合法的括号匹配序列有以下定义:

  1. 空串 "" 是一个合法的括号匹配序列
  2. 如果 "X""Y" 都是合法的括号匹配序列,"XY" 也是一个合法的括号匹配序列
  3. 如果 "X" 是一个合法的括号匹配序列,那么 "(X)" 也是一个合法的括号匹配序列
  4. 每个合法的括号序列都可以由以上规则生成。

例如:"""()""()()""((()))" 都是合法的括号序列

对于一个合法的括号序列我们又有以下定义它的深度:

  1. 空串 "" 的深度是 0
  2. 如果字符串 "X" 的深度是 x,字符串 "Y" 的深度是 y,那么字符串 "XY" 的深度为max(x, y)
  3. 如果 "X" 的深度是 x,那么字符串 "(X)" 的深度是 x+1

例如:
"()()()" 的深度是 1
"((()))" 的深度是3

牛牛现在给你一个合法的括号序列,需要你计算出其深度。

思路:
首先如何判断一个字符串是否是合法的括号序列呢?
一般我们首先会想到:使用栈结构。遍历字符串,遇到左括号保存到栈中;遇到右括号就将栈顶弹出。如果整个遍历过程中栈不为空,且遍历完成后栈为空,那么说明是合法的括号序列。

利用上面的逻辑稍加改造就可以完成本题,在本题中会给出一个合法的括号序列,那么在栈中的左括号数量最多时,就是给出的序列最大的深度。

也可以稍微优化下,不使用栈,而是使用一个变量来记录遍历的最大深度。

  1. import java.util.Stack;
  2. public class ParenthesesDeep {
  3. public static int deep1(String str) {
  4. if (str == null && str == "") return 0;
  5. int deep = 0;
  6. Stack<Character> stack = new Stack<>();
  7. for (int i = 0; i < str.length(); i++) {
  8. if (str.charAt(i) == '(') {
  9. stack.push('(');
  10. } else {
  11. stack.pop();
  12. }
  13. deep = Math.max(deep, stack.size());
  14. }
  15. return deep;
  16. }
  17. public static int deep2(String str) {
  18. if (str == null && str == "") return 0;
  19. int curDeep = 0;
  20. int deep = 0;
  21. for (int i = 0; i < str.length(); i++) {
  22. if (str.charAt(i) == '(') {
  23. curDeep++;
  24. } else {
  25. curDeep--;
  26. }
  27. deep = Math.max(curDeep, deep);
  28. }
  29. return deep;
  30. }
  31. public static void main(String[] args) {
  32. System.out.println(deep1("()()()"));
  33. System.out.println(deep1("((()))"));
  34. System.out.println(deep1("((())((())()))"));
  35. System.out.println(deep2("()()()"));
  36. System.out.println(deep2("((()))"));
  37. System.out.println(deep2("((())((())()))"));
  38. }
  39. }

上面的简直 so easy,那么如果改为这样的问题呢:
给出一堆有左右括号组成的字符串,求出最长合法的括号序列的长度?

思路一:
题意也就是要找到最大的合法子串,那就要知道什么不时候不合法:右括号比左括号多的时候不合法。
在不合法时,记录已找到合法的子串长度,找到最大的长度即可

思路二:
遍历字符串,求出每个右括号结尾的合法字符串的长度,然后找到最大值

代码实现:

  1. // 思路一
  2. public static int maxLength(String str) {
  3. if (str == null && str == "") return 0;
  4. int maxLength = 0;
  5. int curLength = 0;
  6. int leftNum = 0;
  7. for (int i = 0; i < str.length(); i++) {
  8. if (str.charAt(i) == '(') {
  9. leftNum++;
  10. } else {
  11. if (leftNum > 0) {
  12. leftNum--;
  13. curLength += 2;
  14. } else {
  15. curLength = 0;
  16. }
  17. maxLength = Math.max(maxLength, curLength);
  18. }
  19. }
  20. return maxLength;
  21. }
  22. // 思路二
  23. public static int maxLength1(String str) {
  24. if (str == null || str.equals("")) {
  25. return 0;
  26. }
  27. char[] chas = str.toCharArray();
  28. int[] dp = new int[chas.length];
  29. int pre = 0;
  30. int res = 0;
  31. for (int i = 1; i < chas.length; i++) {
  32. if (chas[i] == ')') { // 只有右括号才能结尾
  33. // 假设合法,和 i 匹配的左括号的下标
  34. pre = i - dp[i - 1] - 1;
  35. if (pre >= 0 && chas[pre] == '(') {
  36. // 如果上一个右括号能组成解法序列,那么 i 下标就是 i-i 的结果 + 2 + 上一个子串的长度
  37. dp[i] = dp[i - 1] + 2 + (pre > 0 ? dp[pre - 1] : 0);
  38. }
  39. }
  40. res = Math.max(res, dp[i]);
  41. }
  42. return res;
  43. }

有序栈

请编写一个程序,对一个栈里的整型数据,按升序进行排序(即排序前,栈里的数据是无序的,排序后最大元素位于栈顶),要求最多只能使用一个额外的栈存放临时数据,但不得将元素复制到别的数据结构中。

思路:
一个辅助栈 help,保证该栈中数据有序且小元素在栈顶。
遍历栈 cur = stack.pop();:

  • 如果 help 为空,就直接将 cur 压入 help 中
  • 如果 help 不为空,取 help 栈顶元素和 cur 做比较:
    • 栈顶元素 比 cur 大,将 cur 压入help
    • 栈顶元素 比 cur 小,将栈顶弹出并压入 stack 中,直到新的栈顶比 cur 大,将 cur 压入;或者 help 元素为空
  • 将上述操作执行完成后,stack 为空,help 中所有元素都是有序且栈顶为最小元素,依次将 help 所有元素弹出压入 stack

image.png

代码实现:

  1. import java.util.Stack;
  2. public class SortStack {
  3. public static void sortStack(Stack<Integer> stack) {
  4. if (stack == null || stack.isEmpty()) return;
  5. // 小元素在上的栈
  6. Stack<Integer> minStack = new Stack<>();
  7. while (!stack.isEmpty()) {
  8. Integer top = stack.pop();
  9. while (!minStack.isEmpty() && minStack.peek() < top) {
  10. stack.push(minStack.pop());
  11. }
  12. minStack.push(top);
  13. }
  14. while (!minStack.isEmpty()) stack.push(minStack.pop());
  15. }
  16. public static void main(String[] args) {
  17. Stack<Integer> stack = new Stack<Integer>();
  18. stack.push(3);
  19. stack.push(1);
  20. stack.push(6);
  21. stack.push(2);
  22. stack.push(5);
  23. stack.push(4);
  24. sortStack(stack);
  25. System.out.println(stack.pop());
  26. System.out.println(stack.pop());
  27. System.out.println(stack.pop());
  28. System.out.println(stack.pop());
  29. System.out.println(stack.pop());
  30. System.out.println(stack.pop());
  31. }
  32. }

最大权值

二叉树每个结点都有一个 int 型权值,给定一棵二叉树,要求计算出从根结点到叶结点的所有路径中,权值和最大的值为多少。

思路一:
对于树中任意的节点来说,该节点的最大的路径为 该节点的权值 + 子树中最大的权值

思路二:
使用 map 来保存每条路径的最大值

代码实现:

  1. import java.util.HashMap;
  2. import java.util.Map;
  3. import java.util.Stack;
  4. public class MaxSumInTree {
  5. public static class Node {
  6. public int value;
  7. public Node left;
  8. public Node right;
  9. public Node(int val) {
  10. value = val;
  11. }
  12. }
  13. // 递归
  14. public static int maxSumRecursive(Node head) {
  15. if (head == null) return 0;
  16. return process(head);
  17. }
  18. public static int process(Node node) {
  19. if (node == null) return 0;
  20. int leftData = node.value + process(node.left);
  21. int rightData = node.value + process(node.right);
  22. return Math.max(leftData, rightData);
  23. }
  24. // 非递归
  25. private static int maxSumUnrecursive(Node head) {
  26. if (head == null) return 0;
  27. Stack<Node> stack = new Stack<>();
  28. stack.push(head);
  29. Map<Node, Integer> sumMap = new HashMap<>();
  30. sumMap.put(head, head.value);
  31. int max = 0;
  32. while (!stack.isEmpty()) {
  33. Node cur = stack.pop();
  34. if (cur.left == null && cur.right == null) max = Math.max(max, sumMap.get(cur));
  35. if (cur.right != null) {
  36. sumMap.put(cur.right, sumMap.get(cur) + cur.right.value);
  37. stack.push(cur.right);
  38. }
  39. if (cur.left != null) {
  40. sumMap.put(cur.left, sumMap.get(cur) + cur.left.value);
  41. stack.push(cur.left);
  42. }
  43. }
  44. return max;
  45. }
  46. public static void main(String[] args) {
  47. Node head = new Node(4);
  48. head.left = new Node(1);
  49. head.left.left = new Node(24);
  50. head.left.left.right = new Node(2);
  51. head.left.right = new Node(25);
  52. head.right = new Node(-7);
  53. head.right.left = new Node(3);
  54. head.right.right = new Node(30);
  55. System.out.println(maxSumRecursive(head));
  56. System.out.println(maxSumUnrecursive(head));
  57. }
  58. }