JS data structure and algorithm-Jianzhi offer binary tree algorithm question summary

❗❗ Must-see experience

During the bloggers' brushing questions, basically they will not encounter a binary tree when they encounter a binary tree. Sometimes they will be engaged in a question one afternoon. Even if you can understand the idea of ​​solving other people's problems, write it yourself. I didn't brush it in one go, and instead I first figured out the basic algorithm of the binary tree, and then I picked out all the questions about the binary tree in the sword offer. . Not to mention, this is refreshing. It's no wonder that I didn't have any basic preparation before, so I went directly to the question bank to challenge the question.

What I want to say here is that you must have your own knowledge reserves before brushing questions. For example, the initial data structure must be able to understand it, or the basic data structure has some important points. Do n’t prepare for anything like me. Brushing questions, the more you doubt the life, every question is a blow. Taking the traversal of binary trees, you don't even know how the recursive results in a traversal come out. Even if this algorithm is backed up, you still don't understand it, and it's only three lines of code. Are you sorry to just understand it? In the process of brushing questions, many questions are those three lines of code whose traversal is the focus.

Therefore, the first step of the binary tree is to start by drawing the basic algorithm on paper. It will do more with less. If it is recursive, go back step by step from the end condition, and understand the relationship between the binary tree and the stack and the stack without recursion. Portal-the basic algorithm of binary tree

table of Contents

Portal-Niu Ke.com offer offer

Binary tree structure:

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

Question 4: Rebuilding a binary tree

Difficulty: ♡♡

Pre-middle order

//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
}

Post-middle

//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: Substructure of the tree

Difficulty: ♡♡♡♡

substructure

Title: Enter two binary trees A and B to determine whether B is a substructure of A. (Ps: we agree that the empty tree is not a substructure of any tree)

Idea: The DoesTreeHaveTreefunction is a bit like recursion in preorder traversal, get the parent node value comparison, if they are equal, then compare their left node and right node value are equal

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) 
}

Invert three trees

Original title: buckle 572. Subtree of another tree

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: Mirror image of binary tree

Difficulty: ♡♡

Idea: In-order traversal, exchange the left and right nodes of the current round of nodes every time

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: Print a binary tree from top to bottom

Difficulty: ♡♡♡♡♡

Idea: the hierarchical traversal of the binary tree (breadth first traversal, you can use the queue)

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: Post-order traversal sequence of binary search tree

Difficulty: ♡♡♡♡

Problem: Enter an array of integers to determine whether the array is the result of a traversal of a binary search tree. If yes, output Yes, otherwise output No. Assume that any two numbers in the input array are different from each other.

Idea: Find the law. The last node in the post-order traversal is the root node. The array can be divided into a part that is smaller than the root node value and a part that is larger than the root node. Then recursively. Example: (3 6 5) (9) 7 The
important thing is the end condition of recursion. At first sequence.length <= 1, I thought it was enough to be equal to 1, ignoring the left or right part of the array is empty, such as [6, 5, 9, 7 ] When recursive to [6,5], the left is [] and the right is [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: Binary tree is a path with a certain value

Difficulty: ♡♡♡♡

Problem: Enter the root node and an integer of a binary tree, and print out all paths where the sum of the node values ​​in the binary tree is the input integer. The path is defined as a path starting from the root node of the tree and going down to the leaf node. (Note: In the list of return values, the array with the largest array length is at the top)

Train of thought: the ever-changing sect-traversal in order

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()
}

learn by analogy

What if not from the root node of the tree down to the leaf node, but an arbitrary path?

Reference subtree and substructure

Question 26: Binary search tree and doubly linked list

Difficulty: ♡♡♡

Idea: The point is to use the pointer p to record the previous node. Drawing a picture is easy to understand. Still in the order of traversal

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: The depth of the binary tree

Difficulty: ♡♡

The depth of the tree starts from the root node (its depth is 1) and accumulates layer by layer from top to bottom. The height starts from the leaf node (its height is 1) and accumulates layer by layer from bottom to top. Although the depth and height of the tree are the same, the depth and height of a specific node of the tree are different.

method one:

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

Method Two:

This method starts from the root path and is used in the study of question 24. It is to find an array to record the path. Each time a leaf node is reached, the current path length is calculated and compared with the previous length. Then pop back to the parent node to calculate the length of other paths.

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: Balanced Binary Tree

Difficulty: ♡♡♡

Is an empty tree or a binary sorting tree whose height difference between two left and right subtrees (called the balance factor) is not greater than 1. And the two left and right subtrees are a balanced binary tree.

Idea: firmly grasp the focus of the definition of balanced binary tree, the left and right subtrees are a balanced binary tree

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: The next node of the binary tree

Difficulty: ♡♡♡

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: Symmetric binary tree

Difficulty: ♡♡♡♡♡

Idea: The recursion done before is the recursion of a tree, and now the left and right subtrees of this tree are recursively

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: Print binary tree in zigzag order

Difficulty: ♡♡♡♡

The problem-solving method of this problem is that the node value is saved from left to right according to the number of layers. Some people (yes, it is me) process it on the code of the layer traversal, and discuss the push classification step. Push the left or right, and finally haloed.

The level traversal is to shift out one, and push into its left and right node values. Here, a for loop is added to the while. The wonderful thing is to process the nodes of the same layer . Even if the even layer requires backward output, as long as we have the sequential array of the layer, we only need to reverse the array. Who wants to traverse the nodes of the even layer backwards, is it crazy?

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: Print binary tree as multiple lines

Difficulty: ♡♡♡

Title: Print a binary tree by layers from top to bottom, and nodes of the same layer are output from left to right. Each layer outputs one line.

Just remove the code from the above question about all values ​​in a certain order in reverse order.

Question 61: Serializing a binary tree

Difficulty: ♡♡♡♡

This question wants to vomit, focus on the reconstruction of the binary tree in 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: The k-th smallest node of the binary search tree

Difficulty: ♡♡♡♡

Idea: The kth node is the Kth node in middle-order traversal.

代码需要注意的地方,一开始我将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)
}

Guess you like

Origin www.cnblogs.com/L-xmin/p/12680699.html