前端数据结构与算法——二叉树篇(Javascript版本)

148 阅读6分钟

1.二叉树的前序遍历

//二叉树的递归,用数组接收值
function preorder(root, arr = []) {
    if (root) {
        arr.push(root.val);
        preorder(left);
        preorder(right);
    }
    return arr;
} //中,后递归遍历也是同理,只需要调换位置即可
//二叉树的非递归遍历
function preorder1(root) {
    const stack = [];
    const result = [];
    let current = root;
    while (current || stack.length > 0) {
        while (current) {
            stack.push(current);
            current = current.left;
        }
        current = stack.pop();
        result.push = current.val;
        current = current.right;
    }
    return result;
}
//方法二:
function preorder2(root){
    const stack = [root];
    const result =[];
    while(stack.length > 0){
        let n = stack.pop();
        result.push(n.val);
        if(n.right)stack.push(right);
        if(n.left)stack.push(left);//这里左右相反是因为栈是先进后出,所以将left和right的顺序反过来
    }
    return result;


}

2.二叉树的中序遍历

//二叉树的中序遍历
var inorder = function(root) {
    const stack = [];
    const result = [];
    let current = root;
    while(current || stack.length>0){
        while(current){
        stack.push(current);
        current = current.left;
        }
        current = stack.pop();
        result.push(current.val);
        current = current.right;
    }
    return result;
};

3.二叉树的后序遍历

//通过对前序遍历的逆序输出来实现后序遍历,先序是根左右,后序是根右左,这里需要注意
function postorder(root) {
    if (!root) { return []; }
    const stack = [root];
    const output = [];
    const result = [];
    while (stack.length > 0) {
        let n = stack.pop();
        output.push(n);
        if (n.left) stack.push(n.left);
        if (n.right) stack.push(n.right); //这里的顺序与先序是相反的
    }
    while (output.length) {
        const k = output.pop();
        result.push(k.val);
    }
    return result;
}

4.二叉树的层序遍历

//二叉树的层序遍历,队列,层级这两个是关键点
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 {
            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;
};

5.二叉树的最大深度

//首先深度优先遍历整棵树,记录每层节点的层级,找到层级数最大的那一条路径即可
function maxdeep() {
    var res = 0;

    function dfs(root, l) {
        if (!root) return [];
        if (!root.left && !root.right) res = Math.max(res, l);
        dfs(root.left, l + 1);
        dfs(root.right, l + 1);
    }
    dfs(root, 1);
    return res;
}

6.二叉树的最小深度

//使用广度优先遍历,并记录下层级,当有叶子节点的时候就说明,此时已经到最小深度
var minDepth = function(root) {
    if(!root)return 0;
    const  q = [[root,1]];//这里队列[节点,记录层级]
    while(q.length){
        let [n,level] = q.shift();
        if(!n.left &&!n.right)return level;//当无左右子节点的时候就返回当前层级
        if(n.left) q.push([n.left,level+1]);
        if(n.right) q.push([n.right,level+1]);
    }
};
//总体来说就是用的层序遍历的思路,判定是否有左右子树来返回二叉树的最小深度

7.构建二叉树(根据前序,中序遍历)

例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。 
解题思路:
1.首先先序遍历的第一个节点为根节点,中序遍历从根节点左右分开就是左右子树
2.在根据中序遍历分出的左右子树,来将先序遍历的左右子树分出
3.根据此思路进行递归 
*/
/* 用到了
1.indexOf方法(可返回某个指定的字符串值在字符串中首次出现的位置)
stringObject.indexOf(检索的字符串值,规定在字符串中开始检索的位置)
2.splice方法(从数组添加/删除项目,并返回删除的项目)
array.splice(指定在什么位置添加/删除项目(可为负数), 要删除的项目数, 要添加的项目数)
ex:在位置 2,添加 2 个元素,删除 1 个元素:
fruits.splice(2, 1, "Lemon", "Kiwi");
3.slice方法
array.slice(指定从哪里开始选择,指定结束选择的位置(截至到指定位置的前一个元
    素))
 */
function reConstructBinaryTree(pre, vin) {
    if(pre.length === 0){
        return null;
    }//当先序的长度为0时返回null
    if(pre.length === 1){
        return new TreeNode(pre[0]);
    }//当先序的长度为1时,返回第0项
    const value = pre[0];//首先取先序的第一个节点,这个节点一定是根节点
    const index = vin.indexOf(value);//在中序数组中找到该根节点
    const vinLeft = vin.slice(0,index);//在中序数组中,以从pre数组找到的根节,分为左右两个部分
    const vinRight = vin.slice(index+1);
    const preLeft = pre.slice(1,index+1);//再将第一个根节点剔除出来,在pre数组中
    const preRight = pre.slice(index+1);
    const node = new TreeNode(value);
    node.left = reConstructBinaryTree(preLeft, vinLeft);
    node.right = reConstructBinaryTree(preRight, vinRight);
    return node;
}

8.路径总和

/* 
解决路径总和一共需要三个步骤:
1.用深度优先遍历,遍历每一个节点,获取他们的值(这里使用一个递归函数)
2.当一个节点没有左右子节点的时候,计算总路径值(在函数传入的参数中加一个sum,用来记录路径,当每次右左右子节点的时候,加上这个节点的val值)
3.用一个flag值来记录true,false,当找到最终叶节点的时候且sum=target值,将flag的值改为true; 
*/
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;
};

9.求根节点到叶节点的数字之和

/* 
求根节点到叶节点的数字之和
思路: 深度优先遍历, 用一个数组来存储一条路径上的各数, 再将数组中的数字合并
解题: 不用数组来存储, 每次有左右子节点就把val * 10, 当无左右子树的时候直接加节点值即可
 */
function sumnumber() {
    const dfs = (root, sum) => {
        if (!root) { return 0; }
        sum = sum * 10 + root.val; //这里sum*10+root.val,不可以把顺序弄反了
        if (!root.left && !root.right) {
            return sum;
        }
        return dfs(root.left, sum) + dfs(root.right, sum);
    }
    return dfs(root, 0);

}

10.对称的二叉树

var isSymmetric = function(root) {
    if (!root) { return true; }
    const sym = (left, right) => {
        if (!left && !right) return true;
        if (!left || !right) return false;
        if (left.val !== right.val) return false;
        //这里val实际上左孩子的左子节点的值,还有一个关键点,这里不能当两值相等就返回true,这样的话当第二层的时候若为true后面的都不用比较了
        return sym(left.left, right.right) && sym(left.right, right.left);
        //这里是关键下一次递归是左孩子的左子节点与右孩子的右子节点进行比较
    }
    return sym(root.left, root.right);
};

11.二叉树两节点最近的祖先节点

/*
1.找出两个节点的祖先节点,节点的祖先节点是根节点到该节点路径上的所有节点(包括自己)
2.找到相同的且离的最近的节点即可(即就找两节点最后相同的节点)
下面这个画个图就可以,return可以一直把与p,q相同的root返回回去
*/
const lowestCommonAncestor = (root, p, q) => {
    if (!root) return null;
    // 根节点等于p或q,那么root是最近公共祖先
    if (root === p || root === q) return root;
    // 向左子树寻找节点相同的点
    const left = lowestCommonAncestor(root.left, p, q);
    // 向右子树寻找节点相同的点
    const right = lowestCommonAncestor(root.right, p, q);
    // 若左右各找到一个,那么当前根节点就是最近公共祖先
    if (left && right) return root;
    // 只有左边找到,那么最近公共祖先在左边
    if (left) return left;
    // 只有右边找到,那么最近公共祖先在左边
    if (right) return right;
};

12.合并二叉树

var mergeTrees = function(root1, root2) {
    if (!root1) return root2;
    if (!root2) return root1;
    const root = newTreenode(root1.val + root2.val);
    mergeTrees(root1.left, root2.left);
    mergeTrees(root1.right, root2.right);
    return root;
};

13.二叉树的镜像

function mirror(root) {
    if (root) {
        var temp = root.left;
        root.left = root.right;
        root.right = temp;
        mirror(root.left);
        mirror(root.right);
        return root;
    }
}