1. 程式人生 > >JS資料結構第六篇 --- 二叉樹力扣練習題

JS資料結構第六篇 --- 二叉樹力扣練習題

1、第226題:翻轉二叉樹

遞迴+迭代兩種實現方式:

/** 反轉二叉樹
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 * 第一種方式迭代
 * 執行用時 :72 ms, 在所有 JavaScript 提交中擊敗了87.29%的使用者
 * 記憶體消耗 :33.8 M, 在所有 JavaScript 提交中擊敗了24.26%的使用者
 */
var invertTree = function(root) {
    if (!root) return root;

    var arr = [root];

    while(arr.length){
        var current = arr.shift(); //取出節點,交換左右子節點
        var temp = current.right;
        current.right = current.left;
        current.left = temp;

        //將左右子節點push到陣列中
        if (current.right) arr.push(current.right);
        if (current.left) arr.push(current.left);
    }
    return root;
};


/**
 * 第二種方式遞迴
 * @param root
 * @returns {*}
 * 執行用時 :64 ms, 在所有 JavaScript 提交中擊敗了98.02%的使用者
 * 記憶體消耗 :33.6 MB, 在所有 JavaScript 提交中擊敗了53.85%的使用者
 */
var invertTree2 = function(root) {
    if (!root) return root;

    var temp = invertTree(root.left);
    root.left = invertTree(root.right);
    root.right = temp;
    return root;
};
View Code

 

 2、第144題:二叉樹的前序遍歷

初看這個題目描述,沒怎麼看懂,特別是控制檯的輸入輸出

比如輸入:[3, 9, 20, 15, 7, 88, 16,  2, 19, 13, 26, 11]

輸出是:[3,9,15,2,19,7,13,26,20,88,11,16]

一時沒弄明白,後面琢磨了一下,才發現力扣這裡的輸入是按照輸入順序來組成樹的,而不是按輸入的大小組成樹。

即上面這個輸入的數字列表,做成二叉樹圖為:

如果輸入的數字列表中帶有null, 則null所在的子樹空不佔位,

比如輸入:[3, 9,  null, 20, 15, 7,  88,  16,  2, 19,  null, 13, 26, 11]

輸出為:[3, 9, 20, 7, 19, 88, 13, 26, 15, 16, 11, 2]

輸入數字的二叉樹圖為:

理解了力扣題目的輸入輸出邏輯,咱們再做題,二叉樹的前序遍歷遞迴+迭代方式code (先根節點,再左子節點,再右子節點):

/**  前序遍歷規則:先根節點,再左子節點,再右子節點
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/** 第一種方式:遞迴
 * @param {TreeNode} root
 * @return {number[]}
 執行用時 :72 ms, 在所有 JavaScript 提交中擊敗了86.38%的使用者
 記憶體消耗 :33.8 MB, 在所有 JavaScript 提交中擊敗了17.62%的使用者
 */
var preorderTraversal = function(root) {
    var arr = [];
    recusion(root, arr);
    return arr;

    function recusion(root){
        if (!root) return;
        //前序遍歷,先根節點,再左節點,再右節點
        arr.push(root.val);
        recusion(root.left, arr);
        recusion(root.right, arr);
    }
};

// function TreeNode(val) {
//     this.val = val;
//     this.left = this.right = null;
// }


/**
 * 第二種方式:迭代
 * 執行用時 :76 ms, 在所有 JavaScript 提交中擊敗70.96%的使用者
 * 記憶體消耗 :33.6 MB, 在所有 JavaScript 提交中擊敗了60.62%的使用者
 */
var preorderForeach = function(root) {
    var res = [];
    if (!root) return res;
    var arr = [root];
    while(arr.length){
        //藉助於棧的特性:後進先出
        var current = arr.pop();
        res.push(current.val);

        //先將右節點壓入棧底,因為右節點後取值
        if (current.right){
            arr.push(current.right);
        }
        //左節點先取值,壓入棧頂
        if (current.left){
            arr.push(current.left);
        }
    }
    return res;
};
View Code

 

3、第94題:二叉樹的中序遍歷

 

二叉樹中序遍歷,先找到最左邊的左子節點,從這裡開始,然後左子節點, 再根節點,再右子節點:

/** 中序遍歷:從小到大,從做左邊的左子節點,最後一個是右邊的右子節點
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/** 中序遍歷:按照從小到大排序,先找到最左邊的子節點,也就是最小值,再依次往上走父節點,右節點
 * @param {TreeNode} root
 * @return {number[]}
 * 第一種方式:遞迴
 * 執行用時 :64 ms, 在所有 JavaScript 提交中擊敗了97.67%的使用者
 * 記憶體消耗 :33.8 MB, 在所有 JavaScript 提交中擊敗20.52%的使用者
 */
var inorderTraversal = function(root) {
    const res = [];
    if (!root) return res;
    recusion(root);
    return res;

    function recusion(root){
        if (!root) return;

        recusion(root.left);
        res.push(root.val);
        recusion(root.right);
    }
};


/**
 * 第二種方式:迭代
 * 執行用時 :68 ms, 在所有 JavaScript 提交中擊敗了94.67%的使用者
 * 記憶體消耗 33.7 MB, 在所有 JavaScript 提交中擊敗了30.60%的使用者
 */
var inorderTraversal2 = function(root) {
    const res = [];
    if (!root) return res;

    const arr = [];
    while (root || arr.length){
        while(root){
            arr.push(root);
            root = root.left;
        }
        root = arr.pop();  //最後一個左節點
        res.push(root.val);
        root = root.right;
    }
    return res;
};
View Code

 

4、第145題:二叉樹的後序遍歷

後序遍歷的規則:先葉子節點,再根節點;即先左子節點,再右子節點,再根節點。

/** 後序遍歷規則:先葉子節點,葉子節點先左後右,再根節點
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/** 後序遍歷:先葉子節點,再左子樹,再右子樹
 * 第一種方式:遞迴
 * @param {TreeNode} root
 * @return {number[]}
 * 執行用時 :76 ms, 在所有 JavaScript 提交中擊敗了68.85%的使用者
 * 記憶體消耗 :33.9 MB, 在所有 JavaScript 提交中擊敗了9.84%的使用者
 */
var postorderTraversal = function(root) {
    var res = [];
    if (!root) return res;
    recusion(root);
    return res;

    function recusion(root){
        if (!root) return;

        recusion(root.left);
        recusion(root.right);
        res.push(root.val);
    }
};


/**
 * 第二種方式:迭代
 * @param root
 * @returns {Array}
 * 執行用時 :80 ms, 在所有 JavaScript 提交中擊敗了48.15%的使用者
 * 記憶體消耗 :33.7 MB, 在所有 JavaScript 提交中擊敗25.41%的使用者
 */
var postorderTraversal = function(root) {
    var res = [];
    if (!root) return res;

    var arr = [root];
    while (arr.length){
        var current = arr.pop();
        res.unshift(current.val);

        if (current.left){
            arr.push(current.left);
        }
        if (current.right){
            arr.push(current.right);
        }
    }
    return res;
};
View Code

 

5、第102題:二叉樹的層級遍歷

遞迴層級遍歷和前序遍歷差不多,迭代方式層級遍歷有點繞

/** 層次遍歷
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 * 給定二叉樹: [3,9,20,null,null,15,7],
    3
  / \
 9  20
   /  \
 15   7
 返回其層次遍歷結果:
 [
 [3],
 [9,20],
 [15,7]
 ]

 */
/** 層次遍歷,第一種方式:遞迴, 和前序遍歷差不多
 * @param {TreeNode} root
 * @return {number[][]}
 * 執行用時 :84 ms, 在所有 JavaScript 提交中擊敗了55.19%的使用者
 * 記憶體消耗 :34.6 M, 在所有 JavaScript 提交中擊敗了53.23%的使用者
 */
var levelOrder = function(root) {
    var res = [];
    if (!root) return res;
    recusion(root, 0);
    return res;

    function recusion(root, level){

        if (!root) return;

        if (res[level]){
            res[level].push(root.val);
        }
        else{
            res[level] = [root.val];
        }

        if (root.left){
            recusion(root.left, level+1);
        }
        if (root.right){
            recusion(root.right, level+1);
        }
    }
};


/**
 * 第二種層序遍歷:迭代
 * @param root
 * @returns {Array}
 * 執行用時 :80 ms, 在所有 JavaScript 提交中擊敗了73.64%的使用者
 * 記憶體消耗 :34.8 MB, 在所有 JavaScript 提交中擊敗了28.36%的使用者
 */
var levelOrder2 = function(root) {
    var res = [];
    if (!root) return res;

    var queue = [root];
    while(queue.length){

        //內迴圈把這一層級的所有節點都放入tempQueue佇列中,每一個外迴圈則是每一層級重新開始
        var arr = [], tempQueue = [];
        while(queue.length){
            var current = queue.shift();
            arr.push(current.val);

            if (current.left){
                tempQueue.push(current.left);
            }
            if (current.right){
                tempQueue.push(current.right);
            }
            console.log("tempQueue.length: ", tempQueue.length, ", queue.length: ", queue.length);
            console.log("-----------")
        }
        res.push(arr);
        queue = tempQueue;

        console.log(JSON.stringify(res))
        console.log("***************************")
    }
    return res;
};

// function TreeNode(val){
//     this.val = val;
//     this.left = this.right = null;
// }
//
// var node = new TreeNode(23);
// node.left = new TreeNode(16);
// node.right = new TreeNode(45);
// node.left.left = new TreeNode(3);
// node.left.right = new TreeNode(22);
// node.right = new TreeNode(45);
// node.right.left = new TreeNode(37);
// node.right.right = new TreeNode(99);
// console.log(levelOrder2(node));
View Code

 

6、第104題:二叉樹的最大深度

 

二叉樹的最大深度和求二叉樹的層級遍歷差不多

/**  二叉樹的最大深度
 * 給定一個二叉樹,找出其最大深度。
 二叉樹的深度為根節點到最遠葉子節點的最長路徑上的節點數。
 說明: 葉子節點是指沒有子節點的節點。
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 * 第一種方式:遞迴
 * 執行用時 :96 ms, 在所有 JavaScript 提交中擊敗了51.18%的使用者
 * 記憶體消耗 :37.1 MB, 在所有 JavaScript 提交中擊敗了40.00%的使用者
 */
var maxDepth = function(root) {
    if (!root) return 0;

    var maxLevel = 1;
    recusion(root, 1);
    return maxLevel;

    function recusion(root, level){

        if (level > maxLevel) {
            maxLevel = level;
        }

        if (root.left){
            recusion(root.left, level+1);
        }
        if (root.right){
            recusion(root.right, level+1);
        }
    }
};


/**
 * 第二種:迭代
 * @param root
 * @returns {number}
 執行用時 :88 ms, 在所有 JavaScript 提交中擊敗了81.91%的使用者
 記憶體消耗 :36.8 MB, 在所有 JavaScript 提交中擊敗了93.61%的使用者
 */
var maxDepth2 = function(root) {
    if (!root) return 0;

    var level = 0, queue = [root];
    while(queue.length){
        var tempQueue = [];
        //內迴圈,每次把整個層級節點遍歷完, tempQueue儲存每個層級的所有節點
        while(queue.length){
            var current = queue.shift();

            if (current.left){
                tempQueue.push(current.left);
            }
            if (current.right){
                tempQueue.push(current.right)
            }
        }
        level++;
        queue = tempQueue;
    }
    return level;
};


// function TreeNode(val){
//     this.val = val;
//     this.left = this.right = null;
// }
//
// var node = new TreeNode(3);
// node.left = new TreeNode(9);
// node.right = new TreeNode(20);
// node.right.left = new TreeNode(15);
// node.right.right = new TreeNode(7);
// console.log(maxDepth(node))
View Code

 

7、第662題:二叉樹最大寬度

 

這個題和二叉樹層級遍歷/求最大深度類似,但比層級遍歷要繞要麻煩點。

迭代方式:迭代遍歷,用2個棧,一個用來儲存每一層級的節點,另一個棧用來儲存每個節點的編號。

對節點進行編號,規則:根節點編號從0開始,左子節點編號 = 父節點編號 * 2 + 1, 右子節點編號 = 父節點編號 * 2 + 2;

遞迴方式:規則同迭代方式,也是對節點進行編號

如圖:

/**
 * 給定一個二叉樹,編寫一個函式來獲取這個樹的最大寬度。樹的寬度是所有層中的最大寬度。這個二叉樹與滿二叉樹(full binary tree)結構相同,但一些節點為空。
 每一層的寬度被定義為兩個端點(該層最左和最右的非空節點,兩端點間的null節點也計入長度)之間的長度。

 示例 1:
 輸入:
       1
    /   \
   3     2
  / \     \
 5   3     9
 輸出: 4
 解釋: 最大值出現在樹的第 3 層,寬度為 4 (5,3,null,9)。

 示例 2:
 輸入:
      1
     /
    3
  / \
 5   3
 輸出: 2
 解釋: 最大值出現在樹的第 3 層,寬度為 2 (5,3)。

 示例 3:
 輸入:
     1
    / \
   3   2
  /
 5
 輸出: 2
 解釋: 最大值出現在樹的第 2 層,寬度為 2 (3,2)。

 示例 4:
 輸入:
          1
        / \
       3   2
     /     \
    5       9
   /         \
  6           7
 輸出: 8
 解釋: 最大值出現在樹的第 4 層,寬度為 8 (6,null,null,null,null,null,null,7)。

 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 * 第一種方式:遞迴
 * 執行用時 :84 ms, 在所有 JavaScript 提交中擊敗了100.00%的使用者
 * 記憶體消耗 :36.7 MB, 在所有 JavaScript 提交中擊敗了37.50%的使用者
 */
var widthOfBinaryTree = function(root) {

    if (!root) return 0;

    //queue儲存節點,numArr儲存節點對應的節點編號位置
    var queue = [root], numArr = [0], maxWidth = 1;

    while (queue.length) {
        //tempQueue儲存每一層級所有的節點,tempNumArr儲存對應節點的編號位置
        var tempQueue = [], tempNumArr = [];
        while (queue.length) {
            var node = queue.shift(), num = numArr.shift(); //取出棧底節點和編號

            if (node.left) {
                tempQueue.push(node.left);
                tempNumArr.push(num * 2 + 1);
            }
            if (node.right) {
                tempQueue.push(node.right);
                tempNumArr.push(num * 2 + 2);
            }
        }
        var tempWidth = 0;
        //計算tempNumArr中儲存的這一層的寬度, 最後一位元素儲存這一層級最大寬度的編號
        if (tempNumArr.length) {
            tempWidth = tempNumArr[tempNumArr.length - 1] - tempNumArr[0] + 1;
        }
        if (tempWidth > maxWidth) {
            maxWidth = tempWidth;  //更新最大寬度
        }

        //開始下一個層級的寬度計算
        queue = tempQueue;
        numArr = tempNumArr;
    }

    return maxWidth;
};


/**
 * 第二種遞迴方式:
 * @param root
 * @returns {number}
 * 執行用時 :84 ms, 在所有 JavaScript 提交中擊敗了100.00%的使用者
 * 記憶體消耗 :36 MB, 在所有 JavaScript 提交中擊敗了75.00%的使用者
 */
var widthOfBinaryTree2 = function(root) {

    if (!root) return 0;

    var res = [], maxWidth = 1;
    recusion(root, 0, 0);
    return maxWidth;

    function recusion(root, level, num){

        if (res[level]){
            res[level].push(num);
        }
        else{
            res[level] = [num];
        }

        //計算最大寬度
        var tempArr = res[level];
        var tempWidth = tempArr[tempArr.length - 1] - tempArr[0] + 1;
        if (tempWidth > maxWidth) {
            maxWidth = tempWidth;
        }

        if (root.left){
            recusion(root.left, level + 1, num * 2 + 1);
        }
        if (root.right){
            recusion(root.right, level + 1, num * 2 + 2);
        }
    }
};




// function TreeNode(val){
//     this.val = val;
//     this.left = this.right = null;
// }
//
// //[1,1,1,1,null,null,1,1,null,null,1]
// var root = new TreeNode(1);
// root.left = new TreeNode(1);
// root.right = new TreeNode(1);
// root.left.left = new TreeNode(1);
// root.left.right = new TreeNode(3);
// root.right.right = new TreeNode(9);
// console.log(widthOfBinaryTree(root));
View Code

 

&n