Structure de données JS et algorithme - Jianzhi propose un résumé de la question de l'algorithme d'arbre binaire

❗❗ Expérience incontournable

Pendant les questions de brossage des blogueurs, fondamentalement, ils ne rencontreront pas d'arbre binaire lorsqu'ils rencontrent un arbre binaire. Parfois, ils seront engagés dans une question un après-midi. Même si vous pouvez comprendre l'idée de résoudre les problèmes des autres, écrivez-le vous-même. Je ne l'ai pas brossé en une seule fois, et au lieu de cela, j'ai d'abord trouvé l'algorithme de base de l'arbre binaire, puis j'ai choisi toutes les questions sur l'arbre binaire dans l'offre d'épée, et moins je le serais, plus je m'abuserais au bord de l'effondrement. . Sans oublier, c'est rafraîchissant. Il n'est pas étonnant que je n'avais pas eu de préparation de base auparavant, alors je suis allé directement à la banque de questions pour contester la question.

Ce que je veux dire ici, c'est que vous devez avoir vos propres réserves de connaissances avant de balayer les questions. Par exemple, la structure de données initiale doit être en mesure de la comprendre, ou la structure de données de base a des points importants. Ne vous préparez à rien comme moi. Brosser les questions, plus vous doutez de la vie, chaque question est un coup dur. En prenant la traversée d'arbres binaires, vous ne savez même pas comment les résultats récursifs en sortent. Même si cet algorithme est sauvegardé, vous ne le comprenez toujours pas, et ce ne sont que trois lignes de code. Êtes-vous désolé de simplement le comprendre? Dans le processus de brossage des questions, de nombreuses questions sont ces trois lignes de code dont la traversée est au centre.

Par conséquent, la première étape de l'arbre binaire est de commencer par dessiner l'algorithme de base sur papier, il fera plus avec moins. S'il est récursif, revenez pas à pas à partir de la condition de fin et comprenez la relation entre l'arbre binaire et la pile et la pile sans récursivité. Portal-l'algorithme de base de l'arbre binaire

Annuaire

Offre offre Portal-Niu Ke.com

Structure d'arbre binaire:

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

Question 4: Reconstruire un arbre binaire

Difficulté: ♡♡

Ordre pré-moyen

//pre:[1, 2, 4, 7, 3, 5, 6, 8]
//vin: [4, 7, 2, 1, 5, 3, 8, 6]
function reConstructBinaryTree(pre, vin) {
    let tree = null
    if (pre.length > 1) {
        const root = pre.shift() //从前序遍历头中取出一个的父节点
        const index = vin.indexOf(root)  //父节点位于中序遍历中的位置
        tree = new TreeNode(root)
        tree.left = reConstructBinaryTree(pre.slice(0, index), vin.slice(0, index)) //递归父节点左边的节点
        tree.right = reConstructBinaryTree(pre.slice(index), vin.slice(index + 1))  //递归父节点右边的节点
    } else if (pre.length === 1) {
        tree = new TreeNode(pre[0])
    }
    return tree
}

Après le milieu

//post:[7, 4, 2, 5, 8, 6, 3, 1]
//vin: [4, 7, 2, 1, 5, 3, 8, 6]
function reConstructBinaryTree(post, vin) {
    let tree = null
    if (post.length > 1) {
        const root = post.pop()   //从后序遍历尾中取出一个的父节点
        const index = vin.indexOf(root)  //父节点位于中序遍历中的位置
        tree = new TreeNode(root)
        tree.left = reConstructBinaryTree(post.slice(0, index), vin.slice(0, index)) //递归父节点左边的节点
        tree.right = reConstructBinaryTree(post.slice(index), vin.slice(index + 1))  //递归父节点右边的节点
    } else if (post.length == 1) {
        tree = new TreeNode(post[0])
    }
    return tree
}

Question 17: Sous-structure de l'arbre

Difficulté: ♡♡♡♡

Sous-structure

Titre: Entrez deux arbres binaires A et B pour déterminer si B est une sous-structure de A. (Ps: nous convenons que l'arbre vide n'est une sous-structure d'aucun arbre)

Idée: La DoesTreeHaveTreefonction est un peu comme la récursivité dans la traversée de précommande, obtenez la comparaison de la valeur du nœud parent, si elles sont égales, puis comparez leur nœud gauche et la valeur du nœud droit sont égales

function HasSubtree(pRoot1, pRoot2) {
    let result = false
    if (pRoot1 != null && pRoot2 != null) {
        if (pRoot1.val == pRoot2.val) { //判断父节点
            result = DoesTreeHaveTree(pRoot1, pRoot2)
        }
        if (!result) {//父节点不满足,看看它左节点是否满足
            result = HasSubtree(pRoot1.left, pRoot2)
        }
        if (!result) {//左节点不满足,从其右节点是否满足
            result = HasSubtree(pRoot1.right, pRoot2)
        }
    }
    return result
}
function DoesTreeHaveTree(pRoot1, pRoot2) {
    if (pRoot2 == null) { //root2比到底了,则一定是子结构
        return true
    }
    if (pRoot1 == null) { //root2还没比完,root1就到底了,则一定不是子结构
        return false
    }
    if (pRoot1.val != pRoot2.val) { //节点值不相等
        return false
    }
    //节点值相等,继续比较它们的左右节点值是否相等
    return DoesTreeHaveTree(pRoot1.left, pRoot2.left) && DoesTreeHaveTree(pRoot1.right, pRoot2.right) 
}

Inverser trois arbres

Titre original: boucle 572. Sous-arbre d'un autre arbre

function HasSubtree(pRoot1, pRoot2) {
    let result = false
    if (pRoot1 != null && pRoot2 != null) {
        if (pRoot1.val == pRoot2.val) { //判断父节点
            result = DoesTreeHaveTree(pRoot1, pRoot2)
        }
        if (!result) {
            result = HasSubtree(pRoot1.left, pRoot2)
        }
        if (!result) {
            result = HasSubtree(pRoot1.right, pRoot2)
        }
    }
    return result
}

function DoesTreeHaveTree(pRoot1, pRoot2) {
    //同时到达底部null,才是子树
    if (!pRoot2 && !pRoot1) {
        return true
    }
    //此时已经排除了两者都为null的情况,只要有一个为null则不是
    if (!pRoot2 || !pRoot1) {
        return false
    }
    //没到达底部的时候,没有一个为null
    if (pRoot1.val != pRoot2.val) {
        return false
    }
    //节点值相等,继续比较它们的左右节点值是否相等
    return DoesTreeHaveTree(pRoot1.left, pRoot2.left) && DoesTreeHaveTree(pRoot1.right, pRoot2.right)
}

Question 18: Image miroir d'un arbre binaire

Difficulté: ♡♡

Idée: traversée dans l'ordre, échangez à chaque fois les nœuds gauche et droit du cycle de nœuds actuel

function Mirror(root) {
    if (root === null) {
        return
    }
    const temp = root.left
    root.left = root.right
    root.right = temp
    Mirror(root.left)
    Mirror(root.right)
}

Question 22: Imprimer un arbre binaire de haut en bas

Difficulté: ♡♡♡♡♡

Idée: la traversée hiérarchique de l'arbre binaire (largeur première traversée, vous pouvez utiliser la file d'attente)

function PrintFromTopToBottom(root) {
    // write code here
    let tempTree = []
    let rs = []
    if (root) tempTree.push(root)
    while (tempTree.length) {
        root = tempTree.shift()
        rs.push(root.val)
        if (root.left) tempTree.push(root.left)
        if (root.right) tempTree.push(root.right)
    }
    return rs
}

Question 23: Séquence de traversée post-commande de l'arbre de recherche binaire

Difficulté: ♡♡♡♡

Problème: entrez un tableau d'entiers pour déterminer si le tableau est le résultat d'une traversée d'un arbre de recherche binaire. Si oui, sortie Oui, sinon sortie Non. Supposons que deux nombres quelconques du tableau d'entrée soient différents l'un de l'autre.

Idée: Trouvez la loi. Le dernier nœud de la traversée post-commande est le nœud racine. Le tableau peut être divisé en une partie plus petite que la valeur du nœud racine et une partie plus grande que le nœud racine. Puis récursivement. Exemple: (3 6 5) (9) 7 L'
important est la condition de fin de récursivité. Au début sequence.length <= 1, je pensais que c'était suffisant pour être égal à 1, en ignorant la partie gauche ou droite du tableau est vide, comme [6, 5, 9, 7 ] En récursif vers [6,5], la gauche est [] et la droite est [6]

//sequence:[3, 6, 5, 9, 7]
//sequence:[6, 5, 9, 7]
//sequence:[3, 6, 4, 5, 9, 7]
function VerifySquenceOfBST(sequence) {
    if (sequence.length) {
        return helpVerify(sequence)
    }
    return false
}

function helpVerify(sequence) {
    if (sequence.length <= 1) {//此条件下,递归结束。
        return true
    }
    let index = 0
    const key = sequence[sequence.length - 1]  //后序遍历最后一个是根节点
    while (sequence[index] < key) {   //在数组中查找比根节点小和比根节点大的分界点
        index++
    }
    const pos = index   //记录分界点,此时分界点左边全是小于根节点值的
    while (sequence[index] > key) {   //判断根节点右边是否全部大于根节点值
        index++
    }
    if (index != (sequence.length - 1)) {  //接while
        return false
    }
    //现在有左右两个部分,递归执行
    return helpVerify(sequence.slice(0, pos)) && helpVerify(sequence.slice(pos, sequence.length - 1))
}

Question 24: L'arbre binaire est un chemin avec une certaine valeur

Difficulté: ♡♡♡♡

Problème: entrez le nœud racine et un entier d'un arbre binaire, et imprimez tous les chemins où la somme des valeurs de nœud dans l'arbre binaire est l'entier d'entrée. Le chemin est défini comme un chemin partant du nœud racine de l'arbre et descendant jusqu'au nœud feuille. (Remarque: dans la liste des valeurs de retour, le tableau avec la plus grande longueur de tableau est en haut)

Train de pensée: la traversée de sectes en constante évolution pour

function FindPath(root, expectNumber) {
    // write code here
    let result = []   //存放所有满足条件的路径
    if (root) {
        let path = []    //记录当前路径,当当前路劲满足条件的时候,push进result,
        let currentSum = 0   //记录当前路径的和
        isPath(root, expectNumber, path, result, currentSum)
    }
    return result
}

function isPath(root, expectNumber, path, result, currentSum) {
    currentSum += root.val
    path.push(root.val)

    if (currentSum == expectNumber && !root.left && !root.right) { //根结点开始往下一直到叶结点,当前sum等于目标数
        result.push(path.slice(0))  //注意:这里不能直接push(path),数组是引用类型。也可ES6用法:push([...path])
    }

    if (root.left) { //当前root有左节点
        isPath(root.left, expectNumber, path, result, currentSum)
    }

    if (root.right) { //当前root有右节点
        isPath(root.right, expectNumber, path, result, currentSum)
    }

    // 走到底(叶子)了,无论当前路径满不满足条件,都要回退到父节点继续搜索
    path.pop()
}

Inférence

Et si ce n'est pas du nœud racine de l'arbre jusqu'au nœud feuille, mais un chemin arbitraire?

Sous-arbre et sous-structure de référence

Question 26: Arbre de recherche binaire et liste doublement liée

Difficulté: ♡♡♡

Idée: le but est d'utiliser le pointeur p pour enregistrer le nœud précédent. Dessiner une image est facile à comprendre. Toujours dans l'ordre de traversée

function Convert(pRootOfTree) {
    if (!pRootOfTree) return null
    let p = null //指针,记录前一个结点
    p = ConvertSub(pRootOfTree, p)
    let re = p
    while (re.left) {
        re = re.left
    }
    return re
}

function ConvertSub(pNode, p) {
    if (pNode.left) p = ConvertSub(pNode.left, p);

    if (p == null) {
        p = pNode //找到最左端
    } else {
        p.right = pNode
        pNode.left = p
        p = pNode
    }

    if (pNode.right) p = ConvertSub(pNode.right, p);
    return p
}

Question 38: La profondeur de l'arbre binaire

Difficulté: ♡♡

La profondeur de l'arbre part du nœud racine (sa profondeur est de 1) et s'accumule couche par couche de haut en bas. La hauteur commence à partir du nœud de feuille (sa hauteur est 1) et s'accumule couche par couche de bas en haut. Bien que la profondeur et la hauteur de l'arbre soient identiques, la profondeur et la hauteur d'un nœud spécifique de l'arbre sont différentes.

Première méthode:

function TreeDepth(pRoot) {
    if (!pRoot) return 0;
    var left = 1 + TreeDepth(pRoot.left);
    var right = 1 + TreeDepth(pRoot.right);
    return Math.max(left, right)
}

Deuxième méthode:

Cette méthode part du chemin racine et est utilisée dans l'étude de la question 24. Elle consiste à trouver un tableau pour enregistrer le chemin. Chaque fois qu'un nœud terminal est atteint, la longueur du chemin actuel est calculée et comparée à la longueur précédente. Revenez ensuite au nœud parent pour calculer la longueur des autres chemins.

function TreeDepth(pRoot) {
    // write code here
    let longest = 0
    if (pRoot) {
        let path = []
        longest = getTreeDepth(pRoot, path, longest)
    }
    return longest
}

function getTreeDepth(pRoot, path, longest) {
    path.push(pRoot.val)
    if (!pRoot.left && !pRoot.right && path.length > longest) {
        longest = path.length
    }
    if (pRoot.left) {
        longest = getTreeDepth(pRoot.left, path, longest)
    }
    if (pRoot.right) {
        longest = getTreeDepth(pRoot.right, path, longest)
    }
    path.pop()
    return longest
}

Question 39: Arbre binaire équilibré

Difficulté: ♡♡♡

Est un arbre vide ou un arbre de tri binaire dont la différence de hauteur entre deux sous-arbres gauche et droit (appelée facteur d'équilibre) n'est pas supérieure à 1. Et les deux sous-arbres gauche et droit sont un arbre binaire équilibré.

Idée: saisir fermement l'objectif de la définition d'arbre binaire équilibré, les sous-arbres gauche et droit sont un arbre binaire équilibré

function IsBalanced_Solution(pRoot) {
    if (pRoot == null) {
        return true
    }
    if (Math.abs(TreeDepth(pRoot.left) - TreeDepth(pRoot.right)) > 1) { 
        return false;
    } else { //当前节点的左右高度差不大于1
        return IsBalanced_Solution(pRoot.left) && IsBalanced_Solution(pRoot.right);//判断左右两个子树都是一棵平衡二叉树吗
    }
}

function TreeDepth(pRoot) {
    if (!pRoot) return 0;
    var left = 1 + TreeDepth(pRoot.left);
    var right = 1 + TreeDepth(pRoot.right);
    return Math.max(left, right)
}

Question 57: Le nœud suivant de l'arbre binaire

Difficulté: ♡♡♡

function GetNext(pNode) {
    // write code here
    if (!pNode) {
        return null
    }
    //有右子树的
    if (pNode.right) {
        pNode = pNode.right;
        while (pNode.left) { //下个结点就是其右子树最左边的点
            pNode = pNode.left
        }
        return pNode
    }
    // 没有右子树
    while (pNode.next) { //有父节点
        let p = pNode.next //p指向当前节点的父节点
        if (p.left == pNode) { //直到当前结点是其父节点的左孩子为止
            return p
        }
        pNode = pNode.next
    }
    return null //尾节点
}

Question 58: Arbre binaire symétrique

Difficulté: ♡♡♡♡♡

Idée: La récursion effectuée avant est la récursivité d'un arbre, et maintenant les sous-arbres gauche et droit de cet arbre sont récursivement

function isSymmetrical(pRoot) {
    // write code here
    if (pRoot == null) {
        return true
    }
    return judge(pRoot.left, pRoot.right)
}

function judge(left, right) {
    // 以下判断是否都走到底
    if (left == null) {
        return right == null
    }
    if (right == null) {
        return false
    }
    // 都未走到底
    if (left.val != right.val)
        return false
    return judge(left.left, right.right) && judge(left.right, right.left)
}

Question 59: Imprimer l'arbre binaire en ordre zigzag

Difficulté: ♡♡♡♡

La méthode de résolution de problème de ce problème est que la valeur du nœud est enregistrée de gauche à droite en fonction du nombre de couches. Certaines personnes (oui, c'est moi) la traitent sur le code de la traversée de couche et discutent de l'étape de classification push. Poussez la gauche ou la droite, et enfin un halo.

La traversée de niveau consiste à en déplacer un et à le pousser dans ses valeurs de nœud gauche et droit. Ici, une boucle for est ajoutée au temps. La chose merveilleuse est de traiter les nœuds de la même couche . Même si la couche paire nécessite une sortie en arrière, tant que nous avons le tableau séquentiel de la couche, nous n'avons qu'à inverser le tableau. Qui veut traverser les nœuds de la couche paire vers l'arrière, est-ce fou?

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

function Print(pRoot) {
    if (!pRoot) return []

    let queue = []
    let result = []
    let flag = true //true奇数

    queue.push(pRoot)
    while (queue.length) {
        let tempArr = [] //用来存放当前层所有节点的值
        const len = queue.length //存放当前队列的长度
        for (let i = 0; i < len; i++) {
            let temp = queue.shift();
            tempArr.push(temp.val);
            if (temp.left) {
                queue.push(temp.left);
            }
            if (temp.right) {
                queue.push(temp.right);
            }
        }
        if (!flag) {
            tempArr.reverse();
        }
        flag = !flag;
        result.push(tempArr);
    }
    return result
}

Question 60: Imprimer l'arbre binaire en plusieurs lignes

Difficulté: ♡♡♡

Titre: Imprimer un arbre binaire par calques de haut en bas et les nœuds du même calque sont sortis de gauche à droite. Chaque couche génère une ligne.

Supprimez simplement le code de la question ci-dessus sur toutes les valeurs dans un certain ordre dans l'ordre inverse.

Question 61: Sérialisation d'un arbre binaire

Difficulté: ♡♡♡♡

Cette question veut vomir, se concentrer sur la reconstruction de l'arbre binaire en question 4

function Serialize(pRoot) { 
    if (!pRoot) {
        res.push('#');
    } else {
        res.push(pRoot.val);
        Serialize(pRoot.left);
        Serialize(pRoot.right);
    }
}

function Deserialize(s) {
    if (res.length < 1) return null;
    let node = null;

    let cur = res.shift();
    if (typeof cur == 'number') {
        node = new TreeNode(cur);
        node.left = Deserialize(s);
        node.right = Deserialize(s);
    }
    return node;
}

Question 62: Le k-ème plus petit nœud de l'arbre de recherche binaire

Difficulté: ♡♡♡♡

Idée: Le kième nœud est le Kième nœud en traversée d'ordre moyen.

代码需要注意的地方,一开始我将KthNodeCore(pRoot,<u>k</u>)放在KthNode外,明明和书本里C的代码一样却通不过。
后来发现还是因为JavaScript基本数据类型的传参问题,每次的p值改变必须得return回上一轮递归才能在上一轮递归中取得最新p值,但是该函数中我们还需要返回目标节点,因此最好的解决办法就是将k放于递归函数的上一级作用域中。

占个坑:用非递归写一下
占个坑:第K大呢?

function KthNode(pRoot, k) {
    // write code here
    if (!pRoot || k <= 0)
        return null

    // 为了能追踪k,应该把KthNodeCore函数定义在这里面,k应该在KthNodeCore函数外面
    function KthNodeCore(pRoot) {
        let target = null
        if (pRoot.left) target = KthNodeCore(pRoot.left)
        if (!target) {
            if (k == 1) target = pRoot
            k--
        }
        if (!target && pRoot.right) target = KthNodeCore(pRoot.right)
        return target
    }
    return KthNodeCore(pRoot)
}

Je suppose que tu aimes

Origine www.cnblogs.com/L-xmin/p/12680699.html
conseillé
Classement