青銅三人行之在二叉樹中增加一行

先說一個消息,爲了方便互相交流學習,青銅三人行建了個微信羣,感興趣的夥伴可以掃碼加下面的小助手抱你入羣哦!
青銅三人行小助手

每週一題,代碼無敵~這周「青銅三人行」帶了一個二叉樹的問題:

在二叉樹中增加一行

青銅三人行——每週一題@在二叉樹中增加一行


力扣題目

給定一個二叉樹,根節點爲第1層,深度爲 1。在其第 d 層追加一行值爲 v 的節點。

添加規則:給定一個深度值 d (正整數),針對深度爲 d-1 層的每一非空節點 N,爲 N 創建兩個值爲 v 的左子樹和右子樹。

將 N 原先的左子樹,連接爲新節點 v 的左子樹;將 N 原先的右子樹,連接爲新節點 v 的右子樹。

如果 d 的值爲 1,深度 d - 1 不存在,則創建一個新的根節點 v,原先的整棵樹將作爲 v 的左子樹。

示例 1:
輸入: 
二叉樹如下所示:
       4
     /   \
    2     6
   / \   / 
  3   1 5   

v = 1

d = 2

輸出: 
       4
      / \
     1   1
    /     \
   2       6
  / \     / 
 3   1   5   
示例 2:
輸入: 
二叉樹如下所示:
      4
     /   
    2    
   / \   
  3   1    

v = 1

d = 3

輸出: 
      4
     /   
    2
   / \    
  1   1
 /     \  
3       1
注意
  1. 輸入的深度值 d 的範圍是:[1,二叉樹最大深度 + 1]
  2. 輸入的二叉樹至少有一個節點。

前置知識

題目的關鍵在於找到對應的位置插入節點。而要找到這個“位置”的關鍵就在於找到插入層的所有父節點。因此對於二叉樹的遍歷不太瞭解的小夥伴,建議先去了解一下二叉樹的各種遍歷方式:

二叉樹遍歷_百度百科

遞歸解法

對於二叉樹的遍歷,遞歸是一種常用的解法。在這道題中 helen 就使用了遞歸的方法來找到對應的層級,然後進行節點插入。

設定滿足條件爲進行 d-1 次遞歸,當滿足時,這個節點就爲需要插入節點的父節點。值的注意的是,要記得在每次遞歸時同時對左子樹和右子樹遞歸:

var addOneRow = function (root, v, d) {

    if (d === 1) {
        const head = {
            val: v,
            left: root,
            right: null,
        }
        
        return head;
    }
    function traverse (root, count) {
        if (!root) return;
        
        if (count === d - 1) {
            root.left = {
                val: v,
                left: root.left,
                right: null,
            };
            root.right = {
                val: v,
                right: root.right,
                left: null,
            }
        } else {
            traverse(root.left, count+1);
            traverse(root.right, count+1);
        }
    }
    traverse(root,1);
    return root;
}

遍歷解法

函數遞歸是一種簡化、理解遍歷的方法。但是遞歸的缺點在於有可能在遞歸層級過深的時候造成爆棧的。在所有的程序中,只要有遞歸解法,就一定會存在遍歷解法。因此書香帶來了遍歷的思路。

在其中,先遍歷二叉樹找到 d 層節點,存放入數組中,再次遍歷數組中的節點,插入新的節點:

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} v
 * @param {number} d
 * @return {TreeNode}
 */
var addOneRow = function (root, v, d) {
    function findDNodes(root, d) {
        let resultNodes = [root];
        for (let i = 1; i < d; i++) {
            resultNodes = resultNodes
                .filter(node => node)
                .reduce((sofar, curr) => {
                    return sofar.concat([curr.left, curr.right]);
                }, []);
        }
        return resultNodes;
    }

    if (d === 1) {
        const insertNode = new TreeNode(v);
        insertNode.left = root;
        return insertNode;
    }

    const lastNodes = findDNodes(root, d - 1);
    lastNodes
        .filter(node => node)
        .forEach(node => {
            if (node) {
                const insertNodeL = new TreeNode(v);
                const insertNodeR = new TreeNode(v);
                insertNodeL.left = node.left;
                insertNodeR.right = node.right;
                node.left = insertNodeL;
                node.right = insertNodeR;
            }
        })
    return root;
};

extra

最後,依然是曾大師的 go 語言 show time, 他同樣是遞歸的思路哦~

func addOneRow(root *TreeNode, v int, d int) *TreeNode {
    head := root
    // 跟節點換掉
    if(d==1){
        trRoot := new(TreeNode)
        trRoot.Val = v
        trRoot.Left= root
        trRoot.Right =nil
        return trRoot;
    }
    inorderDepth(root,1,d,v)
    return head
}

func inorderDepth(root *TreeNode,depth int,respected int,v int) {
    // d-1 層爲空節點
    if(root == nil){
        return
    }
    if(respected == depth+1){
        left := root.Left
        right := root.Right

        trLeft := new(TreeNode)
        trLeft.Val = v
        trLeft.Left=left
        trLeft.Right = nil
        root.Left = trLeft

        trRight := new(TreeNode)
        trRight.Val = v
        trRight.Right=right
        trRight.Left = nil
        root.Right = trRight
        return

    }
    // 當深度等於或者超過respected-1時,就不需要遍歷了
    if(respected < depth+1){
        return
    }
    if(root != nil){
        inorderDepth(root.Left,depth+1,respected,v)
        inorderDepth(root.Right,depth+1,respected,v)
    }
}

同樣還是效果驚人:
在這裏插入圖片描述

結束
二叉樹是相對鏈表更復雜一點的數據結構。它的典型用法是對節點定義一個標記函數,將一些值與每個節點相關係。這樣標記的二叉樹就可以實現二叉搜索樹和二叉堆,並應用於高效率的搜索和排序。瞭解這些數據結構會有助於我們實現更高效的程序哦~下週見!


三人行

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章