递归三要素
1.函数所要实现的功能
2.终止的条件
3.找出与函数等价的关系式
二叉树的直径
给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
示例 :
给定二叉树
1
/ \
2 3
/ \
4 5
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
注意:两结点之间的路径长度是以它们之间边的数目表示。
以某节点为根节点的树的直径长度经过的节点数=MAX(子树的直径长度经过的节点数,左子树向下遍历经过最多的节点数+右子树向下遍历经过最多的节点数+1)
以某节点为根节点的树的高度=MAX(左子树高度,右子树高度)+1
var diameterOfBinaryTree = function(root) {
if(root == null || (root.left == null && root.right == null)) return 0
let res = 0
function dfs(root) {
if(root == null) return 0
let left = dfs(root.left)
let right = dfs(root.right)
res = Math.max(res, left + right + 1)
return Math.max(left, right) + 1
}
dfs(root)
return res - 1
}
以某节点为根节点的树的直径长度=MAX(子树的直径长度,左子树的高度+右子树的高度)
var diameterOfBinaryTree = function(root) {
if(root == null || (root.left == null && root.right == null)) return 0
let res = 0
function dfs(root) {
if(root == null) return 0
let left = dfs(root.left)
let right = dfs(root.right)
res = Math.max(res, left + right)
return Math.max(left, right) + 1
}
dfs(root)
return res
}
合并二叉树
给你两棵二叉树: root1 和 root2 。
想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。
示例 1:
输入:root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]
输出:[3,4,5,5,4,null,7]
示例 2:
输入:root1 = [1], root2 = [1,2]
输出:[2,2]
思路:
1.深度优先搜索
root1的左子树=root1的左子树+root2的左子树
root1的右子树=root1的右子树+root2的右子树
var mergeTrees = function (root1, root2) {
if (!root1 && !root2) {
return null;
}
if (!root1) return root2;
if (!root2) return root1;
root1.val = root1.val + root2.val;
root1.left = mergeTrees(root1.left, root2.left);
root1.right = mergeTrees(root1.right, root2.right);
return root1;
};
复杂度分析
时间复杂度:O(min(m,n)),其中 m 和 n 分别是两个二叉树的节点个数。对两个二叉树同时进行深度优先搜索,只有当两个二叉树中的对应节点都不为空时才会对该节点进行显性合并操作,因此被访问到的节点数不会超过较小的二叉树的节点数。
空间复杂度:O(min(m,n)),其中 m 和 n 分别是两个二叉树的节点个数。空间复杂度取决于递归调用的层数,递归调用的层数不会超过较小的二叉树的最大高度,最坏情况下,二叉树的高度等于节点数。
2.广度优先搜索
示例图像
https://leetcode.cn/problems/merge-two-binary-trees/solution/he-bing-er-cha-shu-by-leetcode-solution/
var mergeTrees = function(root1, root2) {
if(root1 === null) return root2;
if(root2 === null) return root1;
let res = new TreeNode(root1.val+root2.val);
let queue = [res];
let queue1 = [root1];
let queue2 = [root2];
while(queue1.length>0&&queue2.length>0){
let node = queue.shift(), node1 = queue1.shift(), node2 = queue2.shift();
let left1 = node1.left, left2 = node2.left, right1 = node1.right, right2 = node2.right;
if(left1 != null || left2 != null){
if(left1!=null&&left2!=null){
node.left = new TreeNode(left1.val + left2.val);
queue.push(node.left);
queue1.push(left1);
queue2.push(left2);
}else if(left1 === null){
node.left = left2;
}else if(left2 === null){
node.left = left1;
}
}
if(right1 != null || right2 != null){
if(right1 != null && right2 != null){
node.right = new TreeNode(right1.val + right2.val);
queue.push(node.right);
queue1.push(right1);
queue2.push(right2);
}else if(right1 === null){
node.right = right2;
}else if(right2 === null){
node.right = right1;
}
}
}
return res;
};
复杂度分析
时间复杂度:O(min(m,n)),其中 m 和 n 分别是两个二叉树的节点个数。对两个二叉树同时进行广度优先搜索,只有当两个二叉树中的对应节点都不为空时才会访问到该节点,因此被访问到的节点数不会超过较小的二叉树的节点数。
空间复杂度:O(min(m,n)),其中 m 和 n 分别是两个二叉树的节点个数。空间复杂度取决于队列中的元素个数,队列中的元素个数不会超过较小的二叉树的节点数。
二叉树的中序遍历
给定一个二叉树的根节点 root ,返回它的中序遍历 。
示例 1:
输入:root = [1,null,2,3]
输出:[1,3,2]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
递归
var inorderTraversal = function(root) {
const res = [];
const inorder = (root) => {
if (!root) {
return;
}
inorder(root.left);
res.push(root.val);
inorder(root.right);
}
inorder(root);
return res;
};
复杂度分析
时间复杂度:O(n),其中 n 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。
空间复杂度:O(n)。空间复杂度取决于递归的栈深度,而栈深度在二叉树为一条链的情况下会达到 O(n) 的级别。
迭代
我写出来的
var inorderTraversal = function(root) {
let vt=[];
let total=[];
while(root!=null){
vt.push(root);
root=root.left;
}
while(vt.length>0){
let s=vt[vt.length-1];
vt.pop();
while(s!=null && s.val!=undefined){
total.push(s.val);
console.log("右",s.val);
s=s.right;
while(s!=null){
vt.push(s);
s=s.left;
}
}
}
return total;
};
标准答案
var inorderTraversal = function(root) {
const res = [];
const stk = [];
while (root || stk.length) {
while (root) {
stk.push(root);
root = root.left;
}
root = stk.pop();
res.push(root.val);
root = root.right;
}
return res;
};
复杂度分析
时间复杂度:O(n),其中 n 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。
空间复杂度:O(n)。空间复杂度取决于栈深度,而栈深度在二叉树为一条链的情况下会达到 O(n) 的级别。
Morris 中序遍历
var inorderTraversal = function(root) {
const res = [];
let predecessor = null;
while (root) {
if (root.left) {
// predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止
predecessor = root.left;
while (predecessor.right && predecessor.right !== root) {
predecessor = predecessor.right;
}
// 让 predecessor 的右指针指向 root,继续遍历左子树
if (!predecessor.right) {
predecessor.right = root;
root = root.left;
}
// 说明左子树已经访问完了,我们需要断开链接
else {
res.push(root.val);
predecessor.right = null;
root = root.right;
}
}
//没有左子树,直接访问该节点,再访问右子树
else {
res.push(root.val);
root = root.right;
}
}
return res;
};