一文圖解樹結構及應用

這是我參與8月更文挑戰的第5天,活動詳情查看:8月更文挑戰前端

什麼是樹?

  • 一種分層數據的抽象模型
  • 常見的數結構: DOM樹、級聯選擇、樹形控件......
  • js中,能夠用object, array構建樹。

例:node

{
    value: "zhe jiang",
    label: "zhe jiang",
    children: [
        value: "hangzhou",
        label: "hangzhou",
        children: [...]
    ]
}

複製代碼

深度/廣度優先遍歷

  • 深度優先遍歷:儘量深的搜索樹的分支
  • 廣度優先遍歷:先訪問離跟節點最近的節點

以下分別是兩種方式 遍歷的順序。json

廣度和深度.png

深度優先遍歷

const tree = {
    val: "a",
    children: [
        {
            val: "b",
            children: [
                { val: "d", children: [] },
                { val: "e", children: [] }
            ]
        },
        {
            val: "c",
            children: [
                { val: "f", children: [] },
                { val: "g", children: [] }
            ]
        }
    ]
}

const dfs = root => {
     console.log(root.val); // a,b,d,e,c,f,g
    root.children.forEach(dfs)
}

複製代碼

深度.png

廣度優先遍歷

const tree = {
    val: "a",
    children: [
        {
            val: "b",
            children: [
                { val: "d", children: [] },
                { val: "e", children: [] }
            ]
        },
        {
            val: "c",
            children: [
                { val: "f", children: [] },
                { val: "g", children: [] }
            ]
        }
    ]
}


經過隊列的方式,將先遍歷的放在隊首,後面層級的放在隊尾
const bfs = root => {
    const q = [root]
    
    while(q.length > 0) {
        const n = q.shift()
        console.log(n.val)
        n.children.forEach(child => {
            q.push(child)
        })
    }
}

複製代碼

廣度.png

二叉樹

什麼是二叉樹

  • 樹中每一個節點最多隻能有兩個子節點
  • 在js中一般用Object來模擬二叉樹

先序遍歷

  • 訪問根節點
  • 對根節點的子樹進行先序遍歷
  • 對跟節點的子樹進行先序遍歷

先序遍歷.png

const bt = {
    val: 1,
    left: {
        val: 2,
        left: {
            val: 4,
            left: null,
            right: null
        },
        right: {
            val: 5,
            left: null,
            right: null
        }
    },
    right: {
        val: 3,
        left: {
            val: 6,
            left: null,
            right: null
        },
        right: {
            val: 7,
            left: null,
            right: null
        }
    }
}

// 遞歸版:
const preorder = root => {
    if (!root) return
    console.log(root.val) // 1245 367
    preorder(root.left)
    preorder(root.right)
}

// 非遞歸版:

const preorder = root => {
    if (!root) return
    const stack = [root]
    while(stack.length) {
        const n = stack.pop()
        console.log(n.val)
        if (n.right) stack.push(n.right)
        if (n.left) stack.push(n.left)
    }
    
}

複製代碼

中序遍歷

  • 對根節點的左子樹進行中序遍歷。
  • 訪問根節點
  • 對根節點的右子樹進行中序遍歷。

中序遍歷.png

const bt = {
    val: 1,
    left: {
        val: 2,
        left: {
            val: 4,
            left: null,
            right: null
        },
        right: {
            val: 5,
            left: null,
            right: null
        }
    },
    right: {
        val: 3,
        left: {
            val: 6,
            left: null,
            right: null
        },
        right: {
            val: 7,
            left: null,
            right: null
        }
    }
}

// 遞歸版:
const inorder = root => {
    if (!root) return
    inorder(root.left)
    console.log(root.val)
    inorder(root.right)
}

複製代碼

後序遍歷

  • 對根節點的左子樹進行中序遍歷。
  • 對根節點的右子樹進行中序遍歷。
  • 訪問根節點

後序遍歷.png

const bt = {
    val: 1,
    left: {
        val: 2,
        left: {
            val: 4,
            left: null,
            right: null
        },
        right: {
            val: 5,
            left: null,
            right: null
        }
    },
    right: {
        val: 3,
        left: {
            val: 6,
            left: null,
            right: null
        },
        right: {
            val: 7,
            left: null,
            right: null
        }
    }
}

const postorder = root => {
    if (!root) return
    postorder(root.left)
    postorder(root.right)
    console.log(root.val)
}

複製代碼

小試牛刀

LeetCode-104: 二叉樹最大深度markdown

**示例:**\
給定二叉樹 `[3,9,20,null,null,15,7]`3
   / \
  9  20
    /  \
   15   7
   
 /** * Definition for a binary tree node. * function TreeNode(val, left, right) { * this.val = (val===undefined ? 0 : val) * this.left = (left===undefined ? null : left) * this.right = (right===undefined ? null : right) * } */
/** * @param {TreeNode} root * @return {number} */
var maxDepth = function(root) {
    let res = 0;
    const dfs = (n, l) => {
        if (!n) return;
        res = Math.max(res, l)
        dfs(n.left, l + 1);
        dfs(n.right, l + 1);
    }
    dfs(root, 1);
    return res
};
複製代碼

LeetCode-111: 二叉樹最小深度app

image.png

輸入:root = [3,9,20,null,null,15,7]
輸出:2
示例 2:

輸入:root = [2,null,3,null,4,null,5,null,6]
輸出:5

來源:力扣(LeetCode)


/** * Definition for a binary tree node. * function TreeNode(val, left, right) { * this.val = (val===undefined ? 0 : val) * this.left = (left===undefined ? null : left) * this.right = (right===undefined ? null : right) * } */
/** * @param {TreeNode} root * @return {number} */
var minDepth = function(root) {
   if (!root) return 0;
    let stack = [ [root, 1] ]
    while(stack.length) {
        const [n, l] = stack.shift();
        console.log(n.val, l)
        if (!n.left && !n.right) return l
        if (n.left) stack.push([n.left, l + 1])
        if (n.right) stack.push([n.right, l + 1])
    }
};

複製代碼

LeetCode-102: 二叉樹的層序遍歷oop

示例:
二叉樹:[3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回其層序遍歷結果:

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

來源:力扣(LeetCode)


/** * Definition for a binary tree node. * function TreeNode(val, left, right) { * this.val = (val===undefined ? 0 : val) * this.left = (left===undefined ? null : left) * this.right = (right===undefined ? null : right) * } */
/** * @param {TreeNode} root * @return {number[][]} */
var levelOrder = function(root) {
    if (!root) return []
    const q = [ [root, 0] ]
    const res = []
    while(q.length) {
        const [n, level] = q.shift()
        if (!res[level]) {
            res.push([n.val])
        } else {
            console.log(res, level)
            res[level].push(n.val)
        }
        if (n.left) q.push([n.left, level + 1])
        if (n.right) q.push([n.right, level + 1])
    }
    return res
};


複製代碼

LeetCode-102: 二叉樹的層序遍歷post

image.png

輸入:root = [1,null,2,3]
輸出:[1,3,2]
示例 2:

輸入:root = []
輸出:[]
示例 3:

輸入:root = [1]
輸出:[1]

來源:力扣(LeetCode)

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var inorderTraversal = function(root) {
    const res = []
    const rec = n => {
        if (!root) return
        rec(n.left)
        res.push(n.val)
        rec(n.right)
    }
    rec(root)
    return res
};


複製代碼

LeetCode-112: 路徑總和ui

image.png

輸入: root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
輸出: true
複製代碼

image.png

輸入:root = [1,2,3], targetSum = 5
輸出:false

示例 3:
輸入:root = [1,2], targetSum = 0
輸出:false

來源:力扣(LeetCode)


/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} targetSum
 * @return {boolean}
 */
var hasPathSum = function(root, targetSum) {
    if (!root) return false;
    let res = false
    const dfs = (n, s) => {
        if (!n.left && !n.right && s === targetSum) {
            res = true
        }
        if (n.left) dfs(n.left, s + n.left.val)
        if (n.right) dfs(n.right, s + n.right.val)
    }
    dfs(root, root.val)
    return res
};
複製代碼

前端與樹

遍歷JSON的全部節點值

const json = {
    a: { b: { c: 1 }  } ,
    d: [1, 2]
}

const dfs = (n, path) => {
    if (!n) return
    console.log(n, path)
    Object.keys(n).forEach(k => {
        dfs(n[k], path.concat(k))
    })
}

dfs(json, [])

複製代碼

總結:this

  • 樹 是一種分層的數據抽象模型
  • 樹的經常使用操做:深度/廣度優先遍歷、先中後序遍歷...
相關文章
相關標籤/搜索