层序遍历
可以借助队列来实现
- 树每层的元素进入队列,开始遍历,逐个弹出
- 每个元素弹出后,就把其左右子树加入队列中
- 每次需要统计size,即每次需要弹出的元素的个数
var levelOrder = function(root) {
//二叉树的层序遍历
let res = [], queue = [];
queue.push(root);
if(root === null) {
return res;
}
while(queue.length !== 0) {
// 记录当前层级节点数,方便之后以此弹出
let length = queue.length;
//存放每一层的节点
let curLevel = [];
for(let i = 0;i < length; i++) {
let node = queue.shift();
// 存放当前弹出的节点
curLevel.push(node.val);
// 存放当前层下一层的节点
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
//把每一层的结果放到结果数组
res.push(curLevel);
}
return res;
};
记住这个模板,分几步做
- 统计当前层的节点数
- 遍历,节点进入队列,再依次出队
- 每个节点出队后都将其左右子节点入队
- 循环,直到队列为空(到了最后一行)
226.翻转二叉树
题目链接:226. 翻转二叉树 - 力扣(LeetCode)
思路
层序遍历,原理和上面的是一样的
在遍历每层的过程中
先把节点的左右子节点互换,再将左右子节点依次入队
var invertTree = function(root) {
let queue = [];
queue.push(root);
if(root === null) {
return root;
}
while(queue.length !== 0) {
// 记录当前层级节点数,方便之后以此弹出
let length = queue.length;
//存放每一层的节点
let curLevel = [];
for(let i = 0;i < length; i++) {
// 当前节点出栈
let node = queue.shift();
// 互换左右节点
if(node.left || node.right){
let i = node.left;
node.left = node.right;
node.right = i;
}
// 存放当前层下一层的节点
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
//把每一层的结果放到结果数组
;
}
return root;
};
另外,本题还可以用深度优先遍历的方法,即前序/中序遍历
深度优先遍历主要是用递归法
递归法的逻辑和上面是一样的,这里把交换节点的逻辑放在 中 来执行
注意
这里前序和后序都比较合适,但是中序遍历不合适
因为,前序是中左右,中序是左中右,后序是左右中
这里中的逻辑是把其左右子节点互换位置,所以中只能在两边而不能在中间
这里画个图可以理解:
因此,代码如下:
-
前序:
var invertTree = function(root) { function dfs(root) { if(root === null){ return root; } swap(root, root.left,root.right) dfs(root.left); dfs(root.right); } dfs(root); function swap(root, l, r){ let i = l; l = r; r = i; root.left = l; root.right = r; } return root; }; -
后序
要注意的点主要在于递归的单层逻辑怎么处理
var invertTree = function(root) { function dfs(root) { if(root === null){ return root; } // 这里把递归的顺序换一下 dfs(root.left); dfs(root.right); swap(root, root.left,root.right) } dfs(root); function swap(root, l, r){ let i = l; l = r; r = i; root.left = l; root.right = r; } return root; };
总结
这题既可以用层次遍历,也可以用深度优先遍历
基本都是在模板上进行修改即可
思路需要想清楚
101. 对称二叉树
题目链接:101. 对称二叉树 - 力扣(LeetCode)
第一想法
层序遍历
遍历到每一层,对其两个节点做一个判断函数
函数作用为对比两个节点的左右子节点是否对称
思路
上面的想法有一点问题,比如碰到这种情况
画起来的两个并不属于同一个元素的左右子节点
这里可以用递归法:
对根节点的两个子树进行遍历
其一遍历方式为左右中
另一个的遍历方式为右左中
如果遍历结果相等,则对称
递归三部曲:
-
确定参数和返回值:
参数是左右节点,返回值是bool类型
-
确定终止条件
左节点为空,右不空,返回false
左不空,右空,返回false
左空右空,返回true
左右都不空但是也不相等,返回false
-
确定单层递归逻辑
比较外侧,左节点的左孩子和右节点的右孩子
比较内侧,右节点的左孩子和左节点的右孩子
如果两侧都是true,则返回true
代码如下:
var isSymmetric = function(root) {
if(root === null) return false;
return compare(root.left, root.right);
};
function compare(l,r){
if(l === null && r === null) return true;
else if(l !== null && r === null) return false;
else if(r !== null && l === null) return false;
else if(l.val !== r.val) return false;
else return compare(l.left,r.right) && compare(l.right, r.left);
}
也可以用层序遍历的方法
入队的逻辑是:
左节点左孩子入队
右节点右孩子入队
左节点右孩子入队
右节点左孩子入队
出队的逻辑是:
每次队伍最前面的两个元素出队
判断是否相等,如果不等就可以直接return false
代码如下:
var isSymmetric = function(root) {
// 迭代方法判断是否是对称二叉树
// 首先判断root是否为空
if(root === null) {
return true;
}
let queue = [];
queue.push(root.left);
queue.push(root.right);
while(queue.length) {
let leftNode = queue.shift(); //左节点
let rightNode = queue.shift(); //右节点
// 这一步是当遍历到底层时,就不需要再有孩子入队了,所以continue
if(leftNode === null && rightNode === null) {
continue;
}
if(leftNode === null || rightNode === null || leftNode.val !== rightNode.val) {
return false;
}
queue.push(leftNode.left); //左节点左孩子入队
queue.push(rightNode.right); //右节点右孩子入队
queue.push(leftNode.right); //左节点右孩子入队
queue.push(rightNode.left); //右节点左孩子入队
}
return true;
};
这里的入队逻辑和之前的不一样,需要注意
以及,虽然每次入队的是4个,出队的是两个
但是有这一步:
if(leftNode === null && rightNode === null) {
continue;
}
保证了可以所有孩子入队并且按顺序依次出队
总结
这一题同时可以用深度优先和层序遍历
在套模板的基础上
需要去好好思考一下解题的思路