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;
};
这一题的两个变种很无聊,都是同样的解法不过把结果翻转一下。
平衡二叉树的判断
判断一棵树是不是平衡二叉树,用分治法来解决:
left
和right
必须都是平衡二叉树- 如果孩子都是平衡二叉树,他们的高度差不能超过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;
};