1. 程式人生 > >JS刷演算法題:二叉樹

JS刷演算法題:二叉樹

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的題,被谷歌公司拒了。。。

谷歌:我們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;
}