左神算法:在二叉树中找到累加和为指定值的最长路径长度(Java版)
本題來自左神《程序員代碼面試指南》“在二叉樹中找到累加和為指定值的最長路徑長度”題目。
題目
給定一棵二叉樹的頭節點 head 和一個 32 位整數 sum,二叉樹節點值類型為整型,求累加和為 sum 的最長路徑長度。“路徑” 是指從某個節點往下,每次最多選擇一個孩子節點或者不選所形成的節點鏈。
例如,二叉樹如圖 3-16 所示。
如果 sum=6,那么累加和為 6 的最長路徑為:-3,3,0,6,所以返回 4。
如果 sum=-9,那么累加和為 -9 的最長路徑為:-9,所以返回 1。
注:本題不用考慮節點值相加可能溢出的情況。
題解
相似問題:“求未排序數組中累加和為規定值的最長子數組長度” 問題
如果二叉樹的節點數為N,本文的解法可以做到時間復雜度為O(N),額外空間復雜度為O(h),其中 h 為二叉樹的高度。
具體過程如下:
1.二叉樹頭節點 head 和規定值 sum 已知;生成變量 maxLen,負責記錄累加和等于 sum 的最長路徑長度。
2.生成哈希表sumMap。負責記錄從 head 開始的一條路徑上的累加和出現的情況,累加和也是從 head 節點的值開始累加的。
- key 值代表某個累加和
- value 值代表這個累加和在路徑中最早出現的層數。
如果在遍歷到 cur 節點時,我們能夠知道從 head 到 cur 節點這條路徑上的累加和出現的情況,那么求以 cur 節點結尾的累加和為指定值的最長路徑長度就非常容易。究竟如何去更新sumMap,才能夠做到在遍歷到任何一個節點時,都能有從 head 到這個節點的路徑上的累加和出現的情況呢?步驟3 詳細地說明了更新過程。
3.首先在 sumMap 中加入一個記錄 (0,0),它表示累加和 0 不用包括任何節點就可以得到。然后按照二叉樹先序遍歷的方式遍歷節點,遍歷到的當前節點記為 cur,從 head 到 cur 父節點的累加和記為 preSum,cur 所在的層數記為level。將 cur.value+preSum 的值記為 curSum,就是從 head 到 cur 的累加和。
- 如果 sumMap 中已經包含 curSum 的記錄,則說明 curSum 在上層中已經出現過,那么就不更新sumMap;
- 如果 sumMap 不包含 curSum 的記錄,則說明curSum 是第一次出現,就把(curSum,level)這個記錄放入sumMap
接下來是求解在必須以 cur 結尾的情況下,累加和為規定值的最長路徑長度,詳細過程這里不再詳述,請讀者閱讀“求未排序數組中累加和為規定值的最長子數組長度”問題。然后是遍歷 cur 左子樹和右子樹的過程,依然按照上述方法使用和更新 sumMap。處理完以cur 為頭節點的子樹,當然要返回 cur 父節點。
在返回前還有一項重要的工作要做,在 sumMap 中查詢 curSum 這個累加和(key)出現的層數(value)
- 如果 value 等于 level,則說明 curSum 這個累加和的記錄是在遍歷到 cur 時加上去的,那就把這條記錄刪除,目的是 保證 maxLen 在遞歸調用返回時不被污染,以便后續調用繼續使用
- 如果 value 不等于 level,則不做任何調整。
4.步驟3 會遍歷二叉樹所有的節點,也會求解以每個節點結尾的情況下,累加和為規定值的最長路徑長度。用 maxLen 記錄其中的最大值即可。
代碼
全部求解過程請參看如下代碼中的 getMaxLength 方法。
package chapter_3_binarytreeproblem;import java.util.HashMap;public class Problem_06_LongestPathSum {public static class Node {public int value;public Node left;public Node right;public Node(int data) {this.value = data;}}public static int getMaxLength(Node head, int sum) {HashMap<Integer, Integer> sumMap = new HashMap<Integer, Integer>();sumMap.put(0, 0); // importantreturn preOrder(head, sum, 0, 1, 0, sumMap);}/*** 前序遍歷,返回滿足要求的累加和sum的最長路徑* @param head 當前頭結點* @param sum 指定的累加和* @param preSum 從head到cur父節點的累加和記為preSum* @param level cur所在的層數* @param maxLen 累加和等于sum的最長路徑長度* @param sumMap (key, value) = (累加和, 累加和在路徑中最早出現的層數)* @return*/public static int preOrder(Node head, int sum, int preSum, int level, int maxLen, HashMap<Integer, Integer> sumMap) {if (head == null) {return maxLen;}int curSum = preSum + head.value;if (!sumMap.containsKey(curSum)) { // curSum在上層未出現過,則記錄sumMap.put(curSum, level); // sumMap中存放的,均為從二叉樹【頭結點】開始計算的累加和}if (sumMap.containsKey(curSum - sum)) { // 如果為true,說明從key所在位置到cur位置的累加和正好為sum// 此步驟求 "以cur結尾的情況下,滿足累加和為sum的最長路徑" 的長度// 即,將新的 "從key所在位置到cur位置的路徑長度" 與 "到達此節點前,已經求得的最長路徑長度" 相比較,取較大者maxLen = Math.max(level - sumMap.get(curSum - sum), maxLen);}maxLen = preOrder(head.left, sum, curSum, level + 1, maxLen, sumMap); // 遞歸前序遍歷左子樹maxLen = preOrder(head.right, sum, curSum, level + 1, maxLen, sumMap); // 遞歸前序遍歷右子樹if (level == sumMap.get(curSum)) { // 如果表中curSum這個累加和的記錄是在遍歷到cur時加上去的,則刪除這條記錄// 這樣做的目的是:保證函數返回后sumMap不受到污染// 因為遞歸調用返回到上層函數后,后續可能還有preOrder()調用,還要用到sumMap// (可以類比"求所有排列組合"題目,也是需要在交換兩元素后、返回前,恢復原狀)sumMap.remove(curSum);}return maxLen;}// for test -- print treepublic static void printTree(Node head) {System.out.println("Binary Tree:");printInOrder(head, 0, "H", 17);System.out.println();}public static void printInOrder(Node head, int height, String to, int len) {if (head == null) {return;}printInOrder(head.right, height + 1, "v", len);String val = to + head.value + to;int lenM = val.length();int lenL = (len - lenM) / 2;int lenR = len - lenM - lenL;val = getSpace(lenL) + val + getSpace(lenR);System.out.println(getSpace(height * len) + val);printInOrder(head.left, height + 1, "^", len);}public static String getSpace(int num) {String space = " ";StringBuffer buf = new StringBuffer("");for (int i = 0; i < num; i++) {buf.append(space);}return buf.toString();}public static void main(String[] args) {Node head = new Node(-3);head.left = new Node(3);head.right = new Node(-9);head.left.left = new Node(1);head.left.right = new Node(0);head.left.right.left = new Node(1);head.left.right.right = new Node(6);head.right.left = new Node(2);head.right.right = new Node(1);printTree(head);System.out.println(getMaxLength(head, 6));System.out.println(getMaxLength(head, -9));System.out.println(getMaxLength(head, 9));}}總結
以上是生活随笔為你收集整理的左神算法:在二叉树中找到累加和为指定值的最长路径长度(Java版)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: leetcode 228. 汇总区间(J
- 下一篇: 左神算法:未排序正数数组中累加和为给定值