【算法】树

最需要重视的是二叉树。

二叉树的深度优先搜索

3中遍历方式:

function dfs(root) {
    if(!root) return

    // 前序遍历
    dfs(root.left)
    // 中序遍历
    dfs(root.right)
    // 后序遍历
}

中序遍历的定义,如果输入的二叉树的根节点不为空,则先遍历它的左子树,然后遍历根节点,最后遍历右子树。

不管是哪种深度优先搜索算法,也不管是递归代码还是迭代代码,如果二叉树有n个节点,那么它们的时间复杂都是O(n)。如果二叉树的深度为h,那么它们的空间复杂度都是O(h)。在二叉树中,二叉树的深度h的最小值是log2(n+1),最大值为n。例如,包含7个节点的二叉树,最少只有3层(二叉树的第1层有1个节点,第2层有2个节点,第3层有4个节点),但最多可能有7层(二叉树中除了叶节点,其他每个节点只有1个子节点)。

面试题47:二叉树剪枝

题目:一棵二叉树的所有节点的值要么是0要么是1,请剪除该二叉树中所有节点的值全都是0的子树。

var pruneTree = function(root) {
    if(root == null) return null;
    root.left = pruneTree(root.left)
    root.right = pruneTree(root.right)

    if(root.left == null && root.right == null && root.val == 0) {
        return null
    }
    return root
};
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
var pruneTree = function(root) {
    const res = dfs(root)
    if(res == 0) return null;
    return root
};

var dfs = function(root) {
    if(!root) return 0
    let left = dfs(root.left)
    let right = dfs(root.right)
    const res = root.val + dfs(root.left) + dfs(root.right)
    if(left == 0) {
        root.left = null
    }
    if(right == 0) {
        root.right = null
    }
    return root.val + left + right;
}

var pruneTree = function(root) {
    if(root == null) return null;
    root.left = pruneTree(root.left)
    root.right = pruneTree(root.right)

    if(root.left == null && root.right == null && root.val == 0) {
        return null
    }
    return root
};

上述代码实质上是实现了递归的后序遍历。每当遍历到一个节点,如果该节点符合条件,则将该节点删除。由于是后序遍历,因此先对根节点root的左右子树递归调用函数pruneTree删除左右子树中节点值全是0的子树。只有当root的左右子树全部为空,并且它自己的值也是0时,这个节点才能被删除。所谓删除一个节点,就是返回null给它的父节点,这样这个节点就从这棵二叉树中消失。

  1. 序列化和反序列化二叉树
题目:请设计一个算法将二叉树序列化成一个字符串,并能将该字符串反序列化出原来二叉树的算法。
  1. 从根节点到叶节点的路径数字之和

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */

/**
 * Encodes a tree to a single string.
 *
 * @param {TreeNode} root
 * @return {string}
 */
var serialize = function(root) {
    return rserialize(root, '');
};

var rserialize = function(root, str) {

    if(root == null) {
        str += "None,"
    } else {
        str += root.val + '' + ",";
        str = rserialize(root.left, str)
        str = rserialize(root.right, str)
    }
    return str
}

/**
 * Decodes your encoded data to tree.
 *
 * @param {string} data
 * @return {TreeNode}
 */
var deserialize = function(data) {
    const dataArray = data.split(",");
    return rdeserialize(dataArray);
};


var rdeserialize = (dataList) => {
    if(dataList[0] === "None") {
        dataList.shift();
        return null;
    }
    const root = new TreeNode(parseInt(dataList[0]));
    dataList.shift();
    root.left = rdeserialize(dataList)
    root.right = rdeserialize(dataList)
    return root;
}
  1. 从根节点到叶节点的路径数字之和
var sumNumbers = function(root) {
    return dfs(root, 0)
};

var dfs = function(root, path) {
    if(root == null) return 0;
    path = path * 10 + root.val
    if(root.left == null && root.right == null) {
        return path
    }
    return dfs(root.left, path) + dfs(root.right, path)

}
  1. 向下的路径节点值之和
题目:给定一棵二叉树和一个值sum,求二叉树中节点值之和等于sum的路径的数目。路径的定义为二叉树中顺着指向子节点的指针向下移动所经过的节点,但不一定从根节点开始,也不一定到叶节点结束。例如,在如图8.5所示中的二叉树中有两条路径的节点值之和等于8,其中,第1条路径从节点5开始经过节点2到达节点1,第2条路径从节点2开始到节点6。
/**
 * @param {TreeNode} root
 * @param {number} targetSum
 * @return {number}
 */
var pathSum = function(root, targetSum) {
    if (root == null) {
        return 0;
    }
    
    let ret = rootSum(root, targetSum);
    ret += pathSum(root.left, targetSum);
    ret += pathSum(root.right, targetSum);
    return ret;
};

const rootSum = (root, targetSum) => {
    let ret = 0;

    if (root == null) {
        return 0;
    }
    const val = root.val;
    if (val === targetSum) {
        ret++;
    } 

    ret += rootSum(root.left, targetSum - val);
    ret += rootSum(root.right, targetSum - val);
    return ret;
}
  1. 节点值之和最大的路径
题目:在二叉树中将路径定义为顺着节点之间的连接从任意一个节点开始到达任意一个节点所经过的所有节点。路径中至少包含一个节点,不一定经过二叉树的根节点,也不一定经过叶节点。给定非空的一棵二叉树,请求出二叉树所有路径上节点值之和的最大值。例如,在如图8.6所示的二叉树中,从节点15开始经过节点20到达节点7的路径的节点值之和为42,是节点值之和最大的路径。

var maxPathSum = function(root) {
    let maxSum = [-Infinity]
    dfs(root, maxSum)
    return maxSum[0]
};


var dfs = function(root, maxSum) {
    if(root == null) return 0
    let maxSumLeft = [-Infinity]
    let maxSumRight = [-Infinity]
    let left = Math.max(0, dfs(root.left, maxSumLeft))
    let right = Math.max(0, dfs(root.right, maxSumRight))

    maxSum[0] = Math.max(maxSumLeft[0], maxSumRight[0]);
    maxSum[0] = Math.max(maxSum[0], root.val + left + right)

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

你可能感兴趣的:(【算法】树)