[Leetcodeは毎週第五科学アルゴリズム質問を行います]

2019年12月6日に書かれたマイクロチャネル公共番号で起動「を念頭に置いて、フロントエンドの成長、」

背景

この記事の文書は、全体の思考プロセスのプロセスは、参考のために、質問を磨きます。主な内容は次のとおりです。

  • トピック分析が想定さ
  • 書き込みコード検証
  • ソリューション他人をチェック
  • 思考の概要

ディレクトリ

イージー

100同じツリー

トピックスアドレス

タイトル説明

2つのバイナリツリーを考えると、それらが同一であるかどうかを確認する関数を記述します。

2は、構造ツリーに同一であり、ノードが同じ値を持っている場合、それらは同一とみなされます。

例:

输入:       1         1
          / \       / \
         2   3     2   3

        [1,2,3],   [1,2,3]

输出: true

输入:      1          1
          /           \
         2             2

        [1,2],     [1,null,2]

输出: false

输入:       1         1
          / \       / \
         2   1     1   2

        [1,2,1],   [1,1,2]

输出: false

トピック分析が想定さ

バイナリであると言う、そして2つの方法でバイナリツリートラバーサルトピックは、直接:深さ優先と幅優先、私は答えにこれら二つを考えました。

書き込みコード検証

Ⅰ。深さ優先

コード:

/**
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {boolean}
 */
var isSameTree = function(p, q) {
    if (p === null && q === null) return true
    if (p === null || q === null) return false

    if (p.val !== q.val) return false

    return isSameTree(p.left, q.left) && isSameTree(p.right, q.right)
};

結果:

  • 渡さ57/57例(52ミリ秒)
  • あなたの実行時には、JavaScriptの提出の98.81パーセントを打ちます
  • あなたのメモリ使用量は、javascriptを提出(33.8メガバイト)の16.66パーセントを打ちます
  • 時間の複雑さO(n)nノードの数など

Ⅱ。幅優先

コード:

/**
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {boolean}
 */
var isSameTree = function(p, q) {
    if (p === null && q === null) return true
    if (p === null || q === null) return false

    let pQ =[p] // 左侧比较队列
    let qQ =[q] // 右侧比较队列

    let res = true

    while(true) {
        if (!pQ.length || !qQ.length) {
            res = pQ.length === qQ.length
            break
        }
        // 当前比较节点
        let curP = pQ.shift()
        let curQ = qQ.shift()
        if ((curP && !curQ) || (!curP && curQ) || (curP && curQ && curP.val !== curQ.val)) {
            res = false
            break
        } else {
            let pL = curP ? curP.left : null
            let pR = curP ? curP.right : null
            if (pL || pR) { // 至少一个存在才有意义
                pQ.push(pL, pR) // 依次推入比较数组,实际上就是广度优先
            }
            let qL = curQ ? curQ.left : null
            let qR = curQ ? curQ.right : null
            if (qL || qR) { // 至少一个存在才有意义
                qQ.push(qL, qR) // 依次推入比较数组,实际上就是广度优先
            }
        }
    }

    return res
};

結果:

  • 渡さ57/57ケース(64ミリ秒)
  • あなたの実行時には、JavaScriptの提出の73.27パーセントを打ちます
  • あなたのメモリ使用量は、javascriptを提出(33.8メガバイト)の15.53パーセントを打ちます
  • 時間の複雑さO(n)nノードの数など

ソリューション他人をチェック

これら二つの考え方は基本的には解決策が見つから異なる方向ではありません。

思考の概要

一般的な問題は、バイナリツリーに遭遇した、またはそれを横断の深さや幅を横断します。深さ優先、また、行きがけとして知られています。

101対称バイナリツリー

トピックスアドレス

タイトル説明

それは鏡面対称である場合には、バイナリツリーを考えると、確認してください。

例えば、バイナリツリーは[1,2,2,3,4,4,3]対称です。

例:

    1
   / \
  2   2
 / \ / \
3  4 4  3

ただし、以下は[1,2,2,null,3,null,3]ミラーイメージではありません。

   1
   / \
  2   2
   \   \
   3    3

説明:

あなたは再帰を使用することができますし、繰り返しがこの問題を解決するための二つの方法であればプラスになります。

トピック分析が想定さ

従来の考え方は、そのトラバーサル、深さ優先または幅優先缶ですので、バイナリツリーは、問題となっています。ミラー対称性は明らかに対称的に等しい別のツリーへの左の単語及び各ツリーの右の部分木の数の値を持つルートノードによって特徴付け観察しました。深さ優先の方法、実際に、タイトルの説明に沿って、再帰の考え方、インチ

書き込みコード検証

Ⅰ。深さ優先

コード:

/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function(root) {
    function isMirror (l, r) {
        if (l === null && r === null) return true
        if (l === null || r === null) return false

        return l.val === r.val && isMirror(l.left, r.right) && isMirror(l.right, r.left)
    }
    return isMirror(root, root)
};

結果:

  • 渡さ195分の195症例(68ミリ秒)
  • あなたの実行時には、JavaScriptの提出の87.74パーセントを打ちます
  • あなたのメモリ使用量は、javascriptを提出(35.5メガバイト)の41.48パーセントを打ちます
  • 時間の複雑さO(n)nノードの数など

Ⅱ。幅優先

コード:

/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function(root) {
    if (root === null) return true
    // 初始队列
    let q = [root.left, root.right]
    // 依次将同级push进队列,每次取两个对称节点进行判断
    while(q.length) {
        let l = q.shift()
        let r = q.shift()
        if (l === null && r === null) continue
        if (l === null || r === null) return false
        if (l.val !== r.val) return false

        q.push(l.left, r.right, l.right, r.left)
    }
    return true
};

結果:

  • 渡さ195分の195症例(64ミリ秒)
  • あなたの実行時には、JavaScriptの提出の94.88パーセントを打ちます
  • あなたのメモリ使用量は、javascriptを提出(35.6メガバイト)の28.3%を打ちます
  • 時間の複雑さO(n)nノードの数など

ソリューション他人をチェック

注文左右の入力配列に加え、層の数にツリーで面白いアイデアを、参照、配列は対称的です。

出力配列のⅠ。左右順

コード:

/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function(root) {
    if (root === null) return true
    // 输出数组
    let arr = []
    search(arr, root, 1);
    // 入参分别为输出,节点和层级
    function search(output, n, k) {
        if (n.left !== null) {
            search(output, n.left, k+1)
        }

        if (n.right !== null) {
            search(output, n.right, k + 1);
        }
    }
     //判断是否对称
     let i = 0, j = arr.length - 1
     while (i < j) {
         if (arr[i] != arr[j]) {
             return false
         }
         i++
         j--
     }
     return true
};

結果:

  • 渡さ195分の195症例(72ミリ秒)
  • あなたの実行時には、JavaScriptの提出の76.3パーセントを打ちます
  • あなたのメモリ使用量は、javascriptを提出(36.3メガバイト)の6.11パーセントを打ちます
  • 時間の複雑さO(n)nノードの数など

思考の概要

問題を解決する方法は、実質的にまたはノードのキューを通過することであるが、再帰の詳細にいくつかの違いになります。左右の思考は効率がはるかに低くなりますが、奇数の非常に明確な出力配列ですが、すべての後に、思考の電車。

最大深さ104バイナリツリー

トピックスアドレス

タイトル説明

その最大の深さを見つけるためのバイナリツリーを考えます。

バイナリツリーの深さは、最長パス最も遠いリーフノード上のノードにルートノードです。

説明:リーフノードは、ノードが子ノードを持たないです。

例:

バイナリツリーを考えると[3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

3のその最大深さに戻ります。

トピック分析が想定さ

この質問は、基本的な考え方は、それぞれの子ノードの深さを計算することで、その後、比較されます。効率を高めるために、最も長いノードとすることができないリーフノードを削除し、右と同じレベルに増加させることができます。

だからここに私は、深さ優先アルゴリズムを達成するために、次のアイデアを使用します。

  • 再帰、最大のサブツリーの高さプラス1に直接アクセス
  • 深い変換層の数を求めるために、キューを使用して

書き込みコード検証

Ⅰ。再帰

コード:

/**
 * @param {TreeNode} root
 * @return {number}
 */
var maxDepth = function(root) {
    if (root === null) return 0
    // 左侧子树的最大高度
    let l = maxDepth(root.left)
    // 右侧子树的最大高度
    let r = maxDepth(root.right)
    return Math.max(l, r) + 1
};

結果:

  • 渡さ39/39例(60ミリ秒)
  • あなたの実行時には、JavaScriptの提出の99%を打ちます
  • あなたのメモリ使用量は、javascriptを提出(37.1メガバイト)の45.77パーセントを打ちます
  • 時間の複雑さO(n)nノードの数など

Ⅱ。キューを使用して

コード:

/**
 * @param {TreeNode} root
 * @return {number}
 */
var maxDepth = function(root) {
    if (root === null) return 0
    // 队列
    let q = [root]
    let dep = 0
    while(q.length) {
        let size = q.length
        dep++
        while(size > 0) {
            let node = q.shift()
            if (node.left !== null) q.push(node.left)
            if (node.right !== null) q.push(node.right)
            size--
        }
    }
    return dep
};

結果:

  • 渡さ39/39ケース(68ミリ秒)
  • あなたの実行時には、JavaScriptの提出の91.33パーセントを打ちます
  • あなたのメモリ使用量は、javascriptを提出(37.2メガバイト)の30.1パーセントを打ちます
  • 時間の複雑さO(n)nノードの数など

ソリューション他人をチェック

最大スタック高さを取って、ここで達成角度でスタックを参照してください、その他の詳細は、基本的に一貫性の違いサイクル、一般的な考え方です。

Ⅰ。使用スタック

コード:

/**
 * @param {TreeNode} root
 * @return {number}
 */
var maxDepth = function(root) {
    if (root === null) return 0
    // 栈
    let s = [{
        node: root,
        dep: 1
    }]
    let dep = 0

    while(s.length) {
        // 先进后出
        var cur = s.pop()
        if (cur.node !== null) {
            let curDep = cur.dep
            dep = Math.max(dep, curDep)
            if (cur.node.left !== null) s.push({node: cur.node.left, dep: curDep + 1})
            if (cur.node.right !== null) s.push({node: cur.node.right, dep: curDep + 1})
        }
    }
    return dep
};

結果:

  • 渡さ39/39ケース(72ミリ秒)
  • あなたの実行時には、JavaScriptの提出の81.41パーセントを打ちます
  • あなたのメモリ使用量は、JavaScriptの提出の66.6パーセントを打つ(37メガバイト)
  • 時間の複雑さO(n)nノードの数など

思考の概要

一般的に深さと広さ優先である二項演算、そう溶液アップに向かって基本的に2つの方向、及び、それを最適化します。

107二分木階層トラバーサルII

トピックスアドレス

タイトル説明

バイナリツリーを考えると、ノードは、ボトムアップ型の値の階層トラバーサルを返します。(すなわち、ルートノードのリーフノードから層への物理層によって、層トラバーサルによって層が左から右へ)

例えば:

バイナリツリーを考えると[3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

そのボトムアップ階層トラバーサルに戻ります:

[
  [15,7],
  [9,20],
  [3]
]

トピック分析が想定さ

この質問は、深さ優先と幅優先、二つの方法で私には思えます。

  • 層の数、反転出力に応じて層の数に相当する深さ最初、各ノードレコード
  • 広まず、各反転出力ノードレコードの後

書き込みコード検証

Ⅰ。深さ優先

コード:

/**
 * @param {TreeNode} root
 * @return {number[][]}
 */
var levelOrderBottom = function(root) {
    // 当前层级标识
    let idx = 0
    let res = []

    function levelOrder(node, floor, arr) {
        if (node === null) return arr
        if(arr[floor]) {
            arr[floor].push(node.val)
        } else {
            arr[floor] = [node.val]
        }
        levelOrder(node.left, floor + 1, arr)
        levelOrder(node.right, floor + 1, arr)
        return arr
    }

    return levelOrder(root, idx, res).reverse()
};

結果:

  • 渡さ34/34ケース(68ミリ秒)
  • あなたの実行時には、JavaScriptの提出の77.01パーセントを打ちます
  • あなたのメモリ使用量は、javascriptを提出(34.7メガバイト)の34.78パーセントを打ちます
  • 時間の複雑さO(n)nノードの数など

Ⅱ。幅優先

コード:

/**
 * @param {TreeNode} root
 * @return {number[][]}
 */
var levelOrderBottom = function(root) {
    if (root === null) return []
    // 初始队列
    let q = [root]
    let res = []

    while(q.length) {
        // 当前层节点数量
        const count = q.length
        let curArr = []
        for(let i = 0; i < count;i++) {
            const node = q.shift()
            curArr.push(node.val)
            // 将子节点依次推入队列
            if (node.left) q.push(node.left)
            if (node.right ) q.push(node.right )
        }
        res.push(curArr)
    }
    return res.reverse()
};

結果:

  • 渡さ34/34ケース(64ミリ秒)
  • あなたの実行時には、JavaScriptの提出の89.2パーセントを打ちます
  • あなたのメモリ使用量は、javascriptを提出(34.7メガバイト)の32.3パーセントを打ちます
  • 時間の複雑さO(n)nノードの数など

ソリューション他人をチェック

任意の特定のソリューションを見ていない、それは主にBFSとDFSによって処理され、いずれかの反復や再帰など。

ここで最初の溶液中で、我々は優先順位や、コードの下の違いを説明することができ、優先度の順で、当然のことながら、プリアンブル優先順位を使用している、何か他のものへの導入:

// 先序,顺序为 根 -> 左 -> 右
function levelOrder(node, floor, arr) {
    if(arr[floor]) {
        arr[floor].push(node.val)
    } else {
        arr[floor] = [node.val]
    }

    levelOrder(node.left, floor + 1, arr)
    levelOrder(node.right, floor + 1, arr)
    return arr
}
// 中序,顺序为 左 -> 根 -> 右
function levelOrder(node, floor, arr) {
    levelOrder(node.left, floor + 1, arr)

   if(arr[floor]) {
       arr[floor].push(node.val)
   } else {
       arr[floor] = [node.val]
   }

    levelOrder(node.right, floor + 1, arr)
    return arr
}
// 后序,顺序为 左 -> 右 -> 根
function levelOrder(node, floor, arr) {
    levelOrder(node.left, floor + 1, arr)
    levelOrder(node.right, floor + 1, arr)

    if(arr[floor]) {
        arr[floor].push(node.val)
    } else {
        arr[floor] = [node.val]
    }
    return arr
}

思考の概要

メリットベースの選択で深さ優先と幅優先で話題の状況に応じてバイナリツリーは、基本的な問題の多くを持っていないことができます。

108二分探索木の順序付けられた配列に変換

トピックスアドレス

タイトル説明

昇順に従って順序付けられた配列は、高度にバランスバイナリ検索ツリーに変換されます。

この問題では、高度にバランスバイナリツリーは二分木は、各ノードの左及び右サブツリーを指すことは、多くて1以上の絶対値の高さの差です。

例:

给定有序数组: [-10,-3,0,5,9],

一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5

トピック分析が想定さ

ノートには2つの点がある:高さは、各ノードのサブツリーの高さの差の絶対値が1以下でないの周りに2つの平衡二分木を必要とし、請求項でバイナリ検索ツリーは、すべてのノードのルート・ノードのサブツリーを左は小さい値、右サブツリーよりすべては、ルートノードよりも大きい値。

指定されたタイトルは、注文した配列である、直接半後検討して処理することができますので、私は再帰答えを指示します:再帰的に左と右のサブツリーを生成し、ルートを見つけます。

書き込みコード検証

Ⅰ。再帰

コード:

/**
 * @param {number[]} nums
 * @return {TreeNode}
 */
var sortedArrayToBST = function(nums) {
    if (!nums.length) return null
    // 中位数,用偏移避免溢出
    const mid = nums.length >>> 1
    const root = new TreeNode(nums[mid])
    root.left = sortedArrayToBST(nums.slice(0, mid))
    root.right = sortedArrayToBST(nums.slice(mid + 1))
    return root
};

結果:

  • 渡さ32/32ケース(80ミリ秒)
  • あなたの実行時には、JavaScriptの提出の70.72パーセントを打ちます
  • あなたのメモリ使用量は、javascriptを提出(37.8メガバイト)の29.79パーセントを打ちます
  • 時間複雑 O(n)

ソリューション他人をチェック

ここを参照してください前順アウトが正確に配列を命じたので、同時に反復処理するためにツリーをトラバースそして、バランスの取れたバイナリツリーを作成するには、別のソリューションです。

Ⅰ。ツリートラバーサルシーケンス配列の割り当てを作成した後

コード:

/**
 * @param {number[]} nums
 * @return {TreeNode}
 */
var sortedArrayToBST = function(nums) {
    if (!nums.length) return null

    // 节点总数
    let len = nums.length
    let root = new TreeNode(-1);
    let q = [root]
    // 已经创建了根节点
    len--
    while(len) {
        const node = q.shift()
        // 左子树
        const l = new TreeNode(-1)
        q.push(l)
        node.left = l
        len--
        if (len) {
            // 右子树
            const r = new TreeNode(-1)
            q.push(r)
            node.right = r
            len--
        }
    }

    let i = 0
    inorder(root)
    function inorder(node) {
        if (node === null) return
        inorder(node.left)
        node.val = nums[i++]
        inorder(node.right)
    }

    return root
};

結果:

  • 渡さ32/32ケース(72ミリ秒)
  • あなたの実行時には、JavaScriptの提出の93.4パーセントを打ちます
  • あなたのメモリ使用量は、javascriptを提出(37.8メガバイト)の24.12パーセントを打ちます
  • 時間複雑 O(n)

思考の概要

ここでは、バイナリ出力配列の前に、アレイは今、バイナリツリーに変換さとなっている、実際には逆の発想です。ただ、プリアンブルシーケンスの後のシーケンス、その上に配列との間の違いについてオンにします。この質問私はまだ再帰半分は解決をお勧めします。

(終わり)


この記事では、オリジナルの記事で、あなたは知識と正しいエラーを更新するので、元のソースを残して再現して、簡単に追跡可能な、知識の古いエラー、より良い読書体験を誤解を避けるために
、あなたはあなたに少し助けを持って来ることができれば、それは、⭐️歓迎スターまたは✏️ フォーク
(明記してくださいソースします。https://chenjiahao.xyz)

おすすめ

転載: www.cnblogs.com/McChen/p/11998902.html