JS刷演算法題:二叉樹

  • 2020 年 2 月 18 日
  • 筆記

Q1.翻轉二叉樹(easy)

如題所示

示例:    輸入:         4     /       2     7   /    /   1   3 6   9    輸出:         4     /       7     2   /    /   9   6 3   1    來源:力扣(LeetCode)  鏈接:https://leetcode-cn.com/problems/invert-binary-tree

這道題目起源於一個非常搞笑的事件:據說大名鼎鼎的Mac軟體包管理工具Homebrew的作者,因為做不出這道在leetcode上難度為easy的題,被Google公司拒了。。。

Google:我們90%的工程師使用您編寫的軟體(Homebrew),但是您卻無法在面試時在白板上寫出翻轉二叉樹這道題,這太糟糕了。

如何看待 Max Howell 被 Google 拒絕?​www.zhihu.com

格式要求

/**   * Definition for a binary tree node.   * function TreeNode(val) {   *     this.val = val;   *     this.left = this.right = null;   * }   */  /**   * @param {TreeNode} root   * @return {TreeNode}   */  var invertTree = function(root) {    // 編碼  };

分析:二叉樹遍歷

思路就是遍歷二叉樹的每一個節點,然後把左右鏈接替換一下就可以了。前序/中序/後序 都可以。如下所示

具體程式碼

var invertTree = function(root) {    traveral(root);    return root;  };    function traveral(node) {    if (node === null) return;    traveral(node.left);    traveral(node.right);    const temp = node.right;    node.right = node.left;    node.left = temp;  }

Q2.二叉樹的右視圖(middle)

給定一棵二叉樹,想像自己站在它的右側,按照從頂部到底部的順序,返回從右側所能看到的節點值。

輸入: [1,2,3,null,5,null,4]  輸出: [1, 3, 4]  解釋:       1            <---   /     2     3         <---            5     4       <---    輸入: [1,2,3,null,5,null,null]  輸出: [1, 3, 5]  解釋:     1            <---   /     2     3         <---       5             <---    來源:LeetCode  鏈接:https://leetcode-cn.com/problems/binary-tree-right-side-view

格式要求

/**   * Definition for a binary tree node.   * function TreeNode(val) {   *     this.val = val;   *     this.left = this.right = null;   * }   */    /**   * @param {TreeNode} root   * @return {number[]}   */  var rightSideView = function(root) {   // 編碼  }

分析:層序遍歷

題目的思路很明顯,對二叉樹進行層序遍歷,然後取得每一層的最後一個節點。放到一個數組裡最後返回。

1.我們可以設置一個隊列存放依次遍歷的節點對象。

2.使用兩層循環

  • 內層循環通過不斷出隊列的方式遍歷當前層的節點,同時通過左右鏈接收集下一層節點
  • 外層循環判斷隊列長度>0時就繼續運行,從而實現逐層迭代

3.在每次內層循環中獲取最右端的非空節點

具體程式碼

var rightSideView = function(root) {    if (!root) return [];    const queue = [];    const arrRS = [];    // 先保存根結點,也就是第一層二叉樹    queue.push(root);    while (queue.length > 0) {      // 將隊列長度先保存到一個變數裡面      // 表示的是上一層的節點的數量      let length = queue.length;      let temp = null;      // 遍歷上一層節點,將它們的子節點加入隊列中,收集得到二叉樹的下一層      for (let i = 0; i < length; i++) {        // 出隊列,並獲得返回的父節點        const node = queue.shift();        // 每次都用當前節點的val覆蓋temp        // temp最後會等於當前層最右的一個非空節點的val值        if (node.val) temp = node.val;        // 收集當前節點的左節點和右節點,從而得到下一層        if (node.left) queue.push(node.left);        if (node.right) queue.push(node.right);      }      // 收集每一層的最右節點      arrRS.push(temp);    }    return arrRS;  };

Q3.二叉樹中的最大路徑和(difficult)

給定一個非空二叉樹,返回其最大路徑和。

本題中,路徑被定義為一條從樹中任意節點出發,達到任意節點的序列。該路徑至少包含一個節點,且不一定經過根節點。

示例1:  輸入: [1,2,3]           1        /        2   3    輸出: 6    示例2:  輸入: [-10,9,20,null,null,15,7]       -10     /     9  20      /       15   7    輸出: 42    來源:LeetCode  鏈接:https://leetcode-cn.com/problems/binary-tree-maximum-path-sum/

格式要求

/**   * Definition for a binary tree node.   * function TreeNode(val) {   *     this.val = val;   *     this.left = this.right = null;   * }   */  /**   * @param {TreeNode} root   * @return {number}   */  var maxPathSum = function(root) {    // 編碼  };

思路分析

1.整體思路:通過後序遍歷,自底向上計算。

因為後序遍歷的計算過程是:左節點-右節點-根結點。 所以通過這種遍歷方式,我們可以在計算兩個子節點的基礎上,推斷當這兩個節點到父節點的最大路徑和。然後不斷向上累加,去計算最大值。

同時在每個節點都通過Math.max更新當前的最大值,直到回歸到根結點的時候,也就能比較出最大值來了。

2.路徑的單一性: 當一個節點是只是作為一個中間節點,而不是一個根節點的時候:左節點和右節點只能選擇一個作為經過的路徑。 因為路徑是「單一」的而不是「分叉」的

例如下面的圖示中, 當我們通過比較選擇9-7-10這條的時候,節點8就不在路徑內了

3.根節點的連接性:當一個節點作為根節點的時候,它可以將兩個子樹的路徑連接起來

4. 對於兩個子節點的累加值A,B,分3種情況討論

  • A>0,B>0: 選擇Math.max(A,B)作為經過路徑
  • A>0,B<0: 選擇A作為經過路徑
  • A<0,B>0: 選擇B作為經過路徑
  • A<0,B<0: A,B都不選

綜上所述

我們的思路是:

  1. 後序遍歷,自底向上計算
  2. 對於每個節點,假設它是根結點,計算它聯合兩個子樹路徑後的最大值
  3. 對於每個節點,假設它是中間節點,選擇兩條中較大的一條子樹作為路徑
  4. 對於2,3分上面的四種情況進行分別處理

具體程式碼

// 1.考慮全為負數的情況  // 2.考慮當前節點為負的情況  let max = Number.MIN_VALUE;  var maxPathSum = function(root) {    max = root.val;    traveral(root);    return max;  };    function traveral(node) {    if (node === null) return 0;    const a = traveral(node.left);    const b = traveral(node.right);    let v = node.val;    if (a >= 0 && b >= 0) {      max = Math.max(max, v + a + b);      v += Math.max(a, b);    } else if (a >= 0) {      max = Math.max(max, v + a);      v += a;    } else if (b >= 0) {      max = Math.max(max, v + b);      v += b;    }    return v;  }    function TreeNode(val) {    this.val = val;    this.left = this.right = null;  }

本文完