常用算法整理:二叉树

DFS – 二叉树的先序,中序和后续遍历

先序遍历

https://leetcode.com/problems/binary-tree-preorder-traversal/
递归解法:

var preorderTraversal = function(root) {
    if(!root) return [];
    return [root.val].concat(preorderTraversal(root.left)).concat(preorderTraversal(root.right));
};

非递归解法:
非递归解法的重点使用一个栈来保存待查找的节点:

var preorderTraversal = function(root) {
    if(!root) return [];
    var result = [],
        stack = [],
        r = root;

    while(r || stack.length) {
        while(r) {
            result.push(r.val);
            stack.push(r);
            r = r.left;
        }
        r = stack.pop();
        r = r.right;
    }

    return result;
}

中序遍历

https://leetcode.com/problems/binary-tree-inorder-traversal/
中序遍历和先序遍历的解法几乎一样,只是在生成result的时候顺序有区别:

递归解法:

var inorderTraversal = function(root) {
    if(!root) return [];
    return inorderTraversal(root.left).concat([root.val]).concat(inorderTraversal(root.right));
};

非递归解法:
和先序遍历几乎一样,只是 result.push(r.val) 的位置不一样。

var inorderTraversal = function(root) {
    if(!root) return [];
    var stack = [],
        result = [],
        r = root;
    while(r || stack.length) {
        while(r) {
            stack.push(r);
            r = r.left;
        }
        r = stack.pop();
        result.push(r.val);
        r = r.right;
    }
};

后序遍历

https://leetcode.com/problems/binary-tree-postorder-traversal/
递归解法:

var postorderTraversal = function(root) {
    if(!root) return [];
    return postorderTraversal(root.left).concat(postorderTraversal(root.right)).concat(root.val);
};

非递归解法:
后序遍历的非递归解法相比于先序和中序会有些复杂,原因:

  • 我们如果发现节点 r 的right 存在,那么我们需要先访问right,也就是需要把 节点 r 再塞回去,在先序和中序中是没有塞回去的做法的。
  • 当我们把塞回去的r再弹出来的时候,其实这个时候因为right已经访问完了,所以不用再塞回去,不然就死循环了,所以还得需要记录 right 是否被访问过来决定是否塞回去

代码如下:

var postorderTraversal = function(root) {
    if(!root) return [];
    var stack = [],
        result = [],
        r = root;
    while(r || stack.length) {
        while(r) {
            stack.push(r);
            r = r.left;
        }
        r = stack.pop();
        if(!r.right || r.right.visited) {
            r.visited = true;
            result.push(r.val);
            r = null;
        } else {
            stack.push(r);
            r = r.right;
        }
    }
    return result;
};

广度优先遍历 – BFS

https://leetcode.com/problems/binary-tree-level-order-traversal/
需要一个 queue 来存储下一层的节点,每次遍历 queue 的时候都生成下一层的节点:

var levelOrder = function(root) {
    if(!root) return [];

    var q = [root],
        result = [];

    while(q.length) {
        var len = q.length;
        var arr = [];
        for(var i=0;i<len;i++) {
            var t = q.shift();
            arr.push(t.val);
            t.left && q.push(t.left);
            t.right && q.push(t.right);
        }
        result.push(arr);
    }

    return result;
};

这一题的两个变种很无聊,都是同样的解法不过把结果翻转一下。

平衡二叉树的判断

判断一棵树是不是平衡二叉树,用分治法来解决:

  • leftright 必须都是平衡二叉树
  • 如果孩子都是平衡二叉树,他们的高度差不能超过1

代码如下:

//用-1表示不平衡,如何平衡则返回高度
var isBalanced = function(root) {
    return height(root) !== -1;
};

var height = function(root) {
    if(!root) return 0;

    var left = height(root.left);
    var right = height(root.right);

    if(left === -1 || right === -1) return -1;
    if(Math.abs(left-right) > 1) return -1;
    return Math.max(left, right)+1;
}

路径和

第一题: https://leetcode.com/problems/path-sum/
判断是否存在一个根节点到叶节点路径之和为指定值,很简单的分治法即可解决,需要注意的一点是对叶节点的判断:

var hasPathSum = function(root, sum) {
    if(!root) return false;
    if(!root.left && !root.right) return sum === root.val;
    if(root.left && hasPathSum(root.left, sum-root.val)) return true;
    if(root.right && hasPathSum(root.right, sum-root.val)) return true;

    return false;
};

第二题:https://leetcode.com/problems/path-sum-ii/
这是第一题的进阶版,需要求出所有的结果,依然是用分治法:

var pathSum = function(root, sum) {
    if(!root) return [];
    var result = [];
    helper(root, sum, [], result);
    return result;
};

var helper = function(root, sum, arr, result) {
    arr = arr.slice(0);
    arr.push(root.val);
    if(!root.left && !root.right) {
        if(sum === root.val) result.push(arr);
        return;
    }
    if(root.left) helper(root.left, sum-root.val, arr.slice(0), result);
    if(root.right) helper(root.right, sum-root.val, arr.slice(0), result);
}

第三题:https://leetcode.com/problems/binary-tree-maximum-path-sum/
找任意路径的最大值,这一题难度比较高,基本思路是这样的:
对每一个节点,分别计算出左子树和右子树的最大值,如果子树不存在或者小于0那么就当做0处理(即不包含),那么就可以计算出包含当前节点的路径的最大值是 Math.max(result, left+right+root.val)
注意一个地方,helper 函数返回的是当前节点和的左子树右子树的最大值,而在计算的是是当前节点和左子树右子树的最大值。为什么会这样呢,因为如果helper函数返回的是左子树与右子树的最大值,那么加上当前节点就会变成一个三叉型,不是一个合法的路径。

var maxPathSum = function(root) {
    if(!root) return 0;
    var result = root.val;

    //包含root节点的最大值
    var helper = function(root) {
        if(!root) return 0;

        var left = Math.max(0, helper(root.left));
        var right = Math.max(0, helper(root.right));

        result = Math.max(result, left+right+root.val);

        return Math.max(left, right) + root.val;

    }

    helper(root, 0);

    return result;
};
    原文作者:lihongxun945
    原文地址: https://blog.csdn.net/lihongxun945/article/details/51307397
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞