三、特殊二叉树问题
2020.11.27
No.101 对称二叉树
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/
2 2
/ \ /
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/
2 2
\
3 3
进阶:
你可以运用递归和迭代两种方法解决这个问题吗?
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/sy… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方案一:
/*
* @lc app=leetcode.cn id=101 lang=javascript
*
* [101] 对称二叉树
*/
// @lc code=start
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isSymmetric = function(root) {
// 中序遍历+层数
const inorderLevelTraversal = function(root) {
const r = [];
const recurse = ( node, n ) => {
if(!node) return;
n++;
recurse(node.left, n);
r.push([node.val,n])
recurse(node.right, n);
};
recurse(root, 0);
return r;
};
const r = inorderLevelTraversal(root);
console.log('r', r);
// 判断中序遍历数组是否对称
let p1 = 0,
p2 = r.length - 1;
while( p1 < p2 ) {
if( r[p1][0] != r[p2][0] || r[p1][1] != r[p2][1] ) return false;
p1++;
p2--;
};
return true;
};
方案二:
/*
* @lc app=leetcode.cn id=101 lang=javascript
*
* [101] 对称二叉树
*/
// @lc code=start
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {boolean}
*/
const isSymmetric = (root) => {
const check = (left, right) => {
if (left == null && right == null) { // 两个子树都为null,是对称的
return true;
}
if (left && right) { // 两个子树都存在,则需要:root值相同,且他们的子树也满足镜像
return left.val == right.val && check(left.left, right.right) && check(left.right, right.left);
}
return false; // 一个子树存在一个不存在,肯定不对称
};
if (root == null) { // 如果传入的root就是null,对称
return true;
}
return check(root.left, root.right); // 否则,判断它的左右子树是否满足对称
};
方案三:
/*
* @lc app=leetcode.cn id=101 lang=javascript
*
* [101] 对称二叉树
*/
// @lc code=start
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {boolean}
*/
const isSymmetric = (root) => {
if (root == null) return true;
const queue = [];
queue.push(root.left, root.right); // 起初入列两个子树
while (queue.length) { // 队列清空就结束,没有节点可入列了
const levelSize = queue.length; // 当前层的节点个数
for (let i = 0; i < levelSize; i += 2) { // 当前层的节点成对出列
const left = queue.shift();
const right = queue.shift(); // 出列一对节点
if ((left && right == null) || (left == null && right)) { // 有一个存在有一个不存在
return false;
}
if (left && right) { // 两个都存在
if (left.val != right.val) { // 节点值不同,不对称
return false;
}
queue.push(left.left, right.right); // 推入下一层的一对节点
queue.push(left.right, right.left); // 推入下一层的一对节点
}
}
}
return true; // bfs结束,始终没有返回false,则返回真
};
有三种解法:1、参考中序遍历思路,判断中序遍历后的数组是否对称,这里需要加入层数来避免有一个节点为null无法判断的情形;2、DFS递归,直接递归判断;3、BFS迭代,维护一个队列进行判断
2020.11.30
No.110 平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:true 示例 2:
输入:root = [1,2,2,3,3,null,null,4,4] 输出:false 示例 3:
输入:root = [] 输出:true
提示:
树中的节点数在范围 [0, 5000] 内 -104 <= Node.val <= 104
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ba… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方案一:
/*
* @lc app=leetcode.cn id=110 lang=javascript
*
* [110] 平衡二叉树
*/
// @lc code=start
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isBalanced = function(root) {
// 获取树的高度
const getTreeHeight = tree => {
if(!tree) return true;
return 1 + Math.max(getTreeHeight(tree.left),getTreeHeight(tree.right))
};
// 差值比较后返回结果
if(!root) return true;
const lh = getTreeHeight(root.left),
rh = getTreeHeight(root.right);
// 如果结果有大于1的直接false
if(Math.abs(lh-rh) > 1 ) {
return false;
}
return isBalanced(root.left) && isBalanced(root.right);
};
方案二:
/*
* @lc app=leetcode.cn id=110 lang=javascript
*
* [110] 平衡二叉树
*/
// @lc code=start
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isBalanced = function(root) {
// 判断是否平衡
const balanced = function (node) {
if (!node) return 0
const left = balanced(node.left)
const right = balanced(node.right)
if (left === -1 || right === -1 || Math.abs(left - right) > 1) {
return -1
}
return Math.max(left, right) + 1
}
return balanced(root) !== -1;
};
有两种方案:1、自顶向下:获取左子树和右子树高度比较,如果不符合直接返回false,否则向下递归;2、自底向上:类似后序遍历方案,先判断左子树,再判断右子树,再判断根节点,有不符合就返回false,否则向上递归
2020.12.01
No.226 翻转二叉树
翻转一棵二叉树。
示例:
输入:
4
/
2 7
/ \ /
1 3 6 9
输出:
4
/
7 2
/ \ /
9 6 3 1
备注:
这个问题是受到 Max Howell 的 原问题 启发的 :
谷歌:我们90%的工程师使用您编写的软件(Homebrew),但是您却无法在面试时在白板上写出翻转二叉树这道题,这太糟糕了。
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/in… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方案一:
/*
* @lc app=leetcode.cn id=226 lang=javascript
*
* [226] 翻转二叉树
*/
// @lc code=start
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {TreeNode}
*/
var invertTree = function(root) {
let temp = null;
const recurse = node => {
if(!node) return;
temp = node.left;
node.left = node.right;
node.right = temp;
recurse(node.left);
recurse(node.right);
};
recurse(root);
return root;
};
方案二:
/*
* @lc app=leetcode.cn id=226 lang=javascript
*
* [226] 翻转二叉树
*/
// @lc code=start
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {TreeNode}
*/
var invertTree = function(root) {
if (!root) return root;
const queue = [root]; // 维护一个队列,初始推入第一层的root
while (queue.length) {
const cur = queue.shift(); // 出列的节点
[cur.left, cur.right] = [cur.right, cur.left]; // 交换左右子树
cur.left && queue.push(cur.left);
cur.right && queue.push(cur.right);
}
return root;
};
面试考树最常考的题,两种方案:1、递归;2、迭代
2020.12.02
No.617 合并二叉树
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
示例 1:
输入:
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
输出:
合并后的树:
3
/
4 5
/ \ \
5 4 7
注意: 合并必须从两个树的根节点开始。
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/me… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方案一:
/*
* @lc app=leetcode.cn id=617 lang=javascript
*
* [617] 合并二叉树
*/
// @lc code=start
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} t1
* @param {TreeNode} t2
* @return {TreeNode}
*/
var mergeTrees = function(t1, t2) {
// 以t1为基准
if(!t1) return t2;
if(!t2) return t1;
t1.val = t2.val + t1.val;
t1.left = mergeTrees(t1.left, t2.left);
t1.right = mergeTrees(t1.right, t2.right);
return t1;
};
方案二:
/*
* @lc app=leetcode.cn id=617 lang=javascript
*
* [617] 合并二叉树
*/
// @lc code=start
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} t1
* @param {TreeNode} t2
* @return {TreeNode}
*/
var mergeTrees = function(t1, t2) {
// 以t1为基准
if(!t1) return t2;
if(!t2) return t1;
t1.val = t2.val + t1.val;
// 构造栈空间迭代
const stack = [[t1, t2]];
while (stack.length) {
const [p, q] = stack.shift();
if (p.left && q.left) {
p.left.val += q.left.val
stack.push([p.left, q.left]);
} else if (!p.left) p.left = q.left;
else if (!q.left) q.left = p.left;
if (p.right && q.right) {
p.right.val += q.right.val;
stack.push([p.right, q.right]);
} else if (!p.right) p.right = q.right;
else if (!q.right) q.right = p.right;
}
return t1;
};
同226,树的基本操作,面试树常考,有两种方案:1、递归;2、迭代
2020.12.03
No.654 最大二叉树
给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:
二叉树的根是数组中的最大元素。 左子树是通过数组中最大值左边部分构造出的最大二叉树。 右子树是通过数组中最大值右边部分构造出的最大二叉树。 通过给定的数组构建最大二叉树,并且输出这个树的根节点。
示例 :
输入:[3,2,1,6,0,5] 输出:返回下面这棵树的根节点:
6
/
3 5
\ /
2 0
1
提示:
给定的数组的大小在 [1, 1000] 之间。
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ma… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方案:
/*
* @lc app=leetcode.cn id=654 lang=javascript
*
* [654] 最大二叉树
*/
// @lc code=start
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {number[]} nums
* @return {TreeNode}
*/
var constructMaximumBinaryTree = function(nums) {
if(nums.length == 0) return null;
const root = new TreeNode(Math.max(...nums));
root.left = constructMaximumBinaryTree(nums.slice(0,nums.indexOf(Math.max(...nums))));
root.right = constructMaximumBinaryTree(nums.slice(nums.indexOf(Math.max(...nums))+1));
return root;
};
典型的递归问题,以最大值作为分割点
2020.12.04
No.655 输出二叉树
在一个 m*n 的二维字符串数组中输出二叉树,并遵守以下规则:
行数 m 应当等于给定二叉树的高度。 列数 n 应当总是奇数。 根节点的值(以字符串格式给出)应当放在可放置的第一行正中间。根节点所在的行与列会将剩余空间划分为两部分(左下部分和右下部分)。你应该将左子树输出在左下部分,右子树输出在右下部分。左下和右下部分应当有相同的大小。即使一个子树为空而另一个非空,你不需要为空的子树输出任何东西,但仍需要为另一个子树留出足够的空间。然而,如果两个子树都为空则不需要为它们留出任何空间。 每个未使用的空间应包含一个空的字符串""。 使用相同的规则输出子树。 示例 1:
输入: 1 / 2 输出: [["", "1", ""], ["2", "", ""]] 示例 2:
输入:
1
/
2 3
4
输出:
[["", "", "", "1", "", "", ""],
["", "2", "", "", "", "3", ""],
["", "", "4", "", "", "", ""]]
示例 3:
输入:
1
/
2 5
/
3
/
4
输出:
[["", "", "", "", "", "", "", "1", "", "", "", "", "", "", ""]
["", "", "", "2", "", "", "", "", "", "", "", "5", "", "", ""]
["", "3", "", "", "", "", "", "", "", "", "", "", "", "", ""]
["4", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]]
注意: 二叉树的高度在范围 [1, 10] 中。
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/pr… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方案:
/*
* @lc app=leetcode.cn id=655 lang=javascript
*
* [655] 输出二叉树
*/
// @lc code=start
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {string[][]}
*/
var printTree = function(root) {
// 获取树的高度
const treeHeight = root => {
if(!root) return 0;
return treeHeight(root.left) > treeHeight(root.right) ? ( treeHeight(root.left) + 1 ): (treeHeight(root.right) + 1);
};
// 填充输出值
const printValue = (r, root, n, left, right ) => {
if(root) {
let mid = Math.floor( ( left + right ) / 2 );
r[n][mid] = root.val + '';
printValue(r, root.left, n+1, left, mid);
printValue(r, root.right, n+1, mid+1, right)}
};
const m = treeHeight(root),
n = Math.pow(2,m) - 1;
console.log(m)
const r = new Array(m);
for(let i=0;i<m;i++) r[i]=new Array(n).fill('');
printValue(r, root,0,0,n);
return r;
};
二分法递归,先生成二维数组,再在对应位置填充值
2020.12.06
No.998 最大二叉树-ii
最大树定义:一个树,其中每个节点的值都大于其子树中的任何其他值。
给出最大树的根节点 root。
就像之前的问题那样,给定的树是从表 A(root = Construct(A))递归地使用下述 Construct(A) 例程构造的:
如果 A 为空,返回 null 否则,令 A[i] 作为 A 的最大元素。创建一个值为 A[i] 的根节点 root root 的左子树将被构建为 Construct([A[0], A[1], ..., A[i-1]]) root 的右子树将被构建为 Construct([A[i+1], A[i+2], ..., A[A.length - 1]]) 返回 root 请注意,我们没有直接给定 A,只有一个根节点 root = Construct(A).
假设 B 是 A 的副本,并附加值 val。保证 B 中的值是不同的。
返回 Construct(B)。
示例 1:
输入:root = [4,1,3,null,null,2], val = 5 输出:[5,4,null,1,3,null,null,2] 解释:A = [1,4,2,3], B = [1,4,2,3,5] 示例 2:
输入:root = [5,2,4,null,1], val = 3 输出:[5,2,4,null,1,null,3] 解释:A = [2,1,5,4], B = [2,1,5,4,3] 示例 3:
输入:root = [5,2,3,null,1], val = 4 输出:[5,2,4,null,1,3] 解释:A = [2,1,5,3], B = [2,1,5,3,4]
提示:
1 <= B.length <= 100
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ma… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方案:
/*
* @lc app=leetcode.cn id=998 lang=javascript
*
* [998] 最大二叉树 II
*/
// @lc code=start
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @param {number} val
* @return {TreeNode}
*/
var insertIntoMaxTree = function(root, val) {
// 中序遍历
const inorderTraversal = function(root) {
let r = [];
// 递归函数
const recurse = root => {
// 递归终止条件
if(!root) return;
// 先遍历左子树
recurse(root.left);
// 遇到终止条件,此时的val是符合终止条件的值
r.push(root.val);
// 再遍历右子树
recurse(root.right);
};
recurse(root);
return r;
};
// 数组的最大二叉树
const constructMaximumBinaryTree = function(nums) {
if(nums.length == 0) return null;
const root = new TreeNode(Math.max(...nums));
root.left = constructMaximumBinaryTree(nums.slice(0,nums.indexOf(Math.max(...nums))));
root.right = constructMaximumBinaryTree(nums.slice(nums.indexOf(Math.max(...nums))+1));
return root;
};
const A = inorderTraversal(root);
A.push(val);
return constructMaximumBinaryTree(A);
};
94题和655题的综合,先求出中序遍历数组,在数组后添加val值,对新数组进行求最大树
总结:
- 特殊二叉树主要是树的不同形态的处理,常见的主要是递归和迭代,面试中常常要求都写出来;
- 根据不同要求获取树,算是树的基本考察,面试过程中如果考树,一般都会以特殊二叉树来检验