二叉树系列算法之【验证二叉树的前序序列化js实现篇】- 暴力与巧思

267 阅读3分钟

这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战

前面我们讲过了二叉树及他的递归算法。# 二叉树递归算法题(经典算法题JavaScript递归实现,持续产出中)。本节我们继续来看LeetCode中难度为中等的这道二叉树算法题。

image.png

递归解法:

因为该题给的入参是二叉树的前序遍历,即root -> left -> right的顺序遍历的二叉树。所以我们可以递归去求当前节点的树长度,当遇到#时结束当前节点子树的长度遍历,拿到整个树的长度与题目给出的入参长度比较。

如图所示:

image.png

image.png

image.png

image.png

image.png

代码如下:

const isValidSerialization = function (preorder) {
    // 判断是否数组越界,越界表示想去查下一个节点但是给定的数组中缺少该节点
    let isOut = false;
    const arr = preorder.split(',');
    const len = getLenFunc(0, arr);

    // 递归函数,表示从idx节点开始的树长度
    function getLenFunc (idx, array) {
        // 如果想查询的下标越界返回0,并置为数组越界,后续递归也不需要进行了
        if (isOut || idx >= array.length) {
            isOut = true;
            return 0;
        }
        // 如果查询的节点是空节点,返回1
        if (array[idx] === '#') return 1;
        // 左子树的下标往后移动一位
        const leftLen = getLenFunc(1 + idx, array);
        // 右子树的起始下标为往后移动1+左子树的长度,如上图所示
        const rightLen = getLenFunc(1 + idx + leftLen, array);
        // 树长度为当前节点+左子树长度+右子树长度
        return 1 + leftLen + rightLen;
    }


    return !isOut && len === arr.length;
}

image.png

结合栈解法

我们看到,一棵树遍历到叶子节点,必定是node->#->#,那如果我们把这个叶子节点当做一个空节点呢,相当于从下往上砍树,能正好砍完就说明是合理的树结构。

我们从根节点开始遍历,往一个栈中压入节点,当压入2个#时,把2个空节点连同叶子节点一起出栈,再压入一个空节点#进去,直到遍历完栈内数据为[#]表示是一个合理的树结构。

如图所示: 压入4,#,#时,#,#,4出栈,再压入#

image.png

image.png

代码如下:

const isValidSerialization = function (preorder) {
    const stack = [];
    const array = preorder.split(',');

    for (let i = 0; i < array.length; i++) {
        stack.push(array[i])
        while (stack.length >= 3 && stack[stack.length - 1] === '#' && stack[stack.length - 2] === '#' && stack[stack.length-3] !== '#') {
            stack.pop();
            stack.pop();
            stack[stack.length - 1] = '#';
        }
    }
    return stack.length === 1 && stack[0] === '#';
}

image.png

出入度

再优化一下??上个算法的空间复杂度是O(N),我们需要一个建立一个栈,我们能不能使用O(1)实现?

我们可以想到,一个普通节点有1个入度,2个出度,那我们维护一个出入度总计值diff(ps: diff初始值是1,因为当前空间需要一个出度,来塞入root节点,否则root节点没有地方塞),即当节点是数字时,-1入度,+2出度,当节点是#时,-1入度。当节点都被填满,应该出入度总额是0。

具体代码就不写了,希望大家能自己思考下,写个伪代码放到讨论区,我会积极回复的~

有更好更多的方案也请不吝赐教~

😀😀😀欢迎大家讨论,看完记得点个👍🏻哦。本文的代码,包括出入度方案,已放Github,里面还有之前算法的代码,后续代码也会陆续更新。