提示:努力生活,开心、快乐的一天
文章目录
二叉树层序遍历
思路
层序遍历
一个二叉树。就是从左到右一层一层的去遍历二叉树,
需要借用一个辅助数据结构即队列来实现
,队列先进先出
,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑
102.二叉树的层序遍历
💡解题思路
- 定义
一个数组(二维数组)
,返回最终结果;定义一个队列
,并将root头结点加入队列中 二叉树非空判断
- 开始循环,
循环的条件是队列中不为空
;获取队列的长度,也就是每层的结点数
;定义一个数组,存放每一层的结点
- 根据结点数进行遍历,将
队列的头部元素弹出
,并加入到数组中,判断弹出的元素的左右结点是否存在,存在的话加入到队列中
- 每次循环将每层的数组加入到最终的返回结果中
🤔遇到的问题
- 队列不为空的判断,不能使用!queue,使用queue.length!==0
- 二叉树不为空,判断的应该是!root,而不是!stack
💻代码实现
var levelOrder = function (root) {
//二维数组
let res = []
//队列
let queue = []
//加入头节点
queue.push(root)
//二叉树不为空的判断
if (!root) return res
//队列为空,结束循环
while (queue.length!==0) {
//每层的节点数
let len = queue.length
//每层的节点数组
let cur = []
//根据节点数进行遍历
for (let i = 0; i < len; i++){
//弹出队列
let node = queue.shift()
cur.push(node.val)
// 存放当前层下一层的节点
//左右结点存在,并加入队列中
node.left && queue.push(node.left)
node.right&&queue.push(node.right)
}
//每层的数组加入到总的二维数组中
res.push(cur)
}
return res
};
🎯题目总结
- 存放每一层的结点数
- 根据节点数进行遍历时,弹出相应数量的结点的同时,存放当前层下一层的节点
226.翻转二叉树
💡解题思路
- 只要
把每一个节点的左右孩子翻转一下
,就可以达到整体翻转的效果 - 递归遍历:遵循递归三部曲
确定递归函数的参数和返回值
:参数就是要传入节点的指针,不需要其他参数了确定终止条件
:当前节点为空的时候,就返回确定单层递归的逻辑
:因为是先前序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树
- 层序遍历:在向栈中添加左右结点之前,先将左右结点进行交换
🤔遇到的问题
- 交换左右结点出错
💻代码实现
递归遍历(前序遍历)
var invertTree = function(root) {
//终止条件
if(!root) return null
//交换左右节点
let leftNode = root.left
root.left = invertTree(root.right)
root.right = invertTree(leftNode)
return root
};
层序遍历
var invertTree = function (root) {
const invertNode = (root, left, right) => {
let temp = left;
left = right
right = temp
root.left = left
root.right = right
}
if (!root) return root
let queue = []
queue.push(root)
while (queue.length) {
let len = queue.length
for (let i = 0; i < len; i++) {
let node = queue.shift()
invertNode(node, node.left, node.right)
node.left && queue.push(node.left)
node.right && queue.push(node.right)
}
}
return root
};
🎯题目总结
层序遍历的交换左右结点需要注意
101. 对称二叉树
💡解题思路
- 二叉树是否对称,要比较的是
根节点的左子树与右子树是不是相互翻转的
,要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树,如何进行比较?比较的是两个子树的里侧和外侧的元素是否相等
- “后序遍历”:遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中
递归法(后序遍历)
:递归三部曲
- 确定递归函数的参数和返回值:因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点
- 确定终止条件:左节点为空,右节点不为空,不对称,return false;左不为空,右为空,不对称 return false;左右都为空,对称,返回true;左右都不为空,比较节点数值,不相同就return false
- 确定单层递归的逻辑:此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。1、比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子;2、比较内侧是否对称,传入左节点的右孩子,右节点的左孩子;3、如果左右都对称就返回true ,有一侧不对称就返回false
迭代法
:使用队列来比较两个树(根节点的左右子树)是否相互翻转
,(注意这不是层序遍历)
迭代法,其实是把左右两个子树要比较的元素顺序放进一个容器
,然后成对成对的取出来进行比较
,那么其实使用栈也是可以的
🤔遇到的问题
- 递归遍历:终止条件判断是否为null不能用!leftNode
- 迭代法:两个结点一起比较,同时存,同时取
💻代码实现
递归遍历(后序遍历)
var isSymmetric = function (root) {
// 使用递归遍历左右子树 递归三部曲
// 1. 确定递归的参数 root.left root.right和返回值true false
const compare = (left, right) => {
// 2. 确定终止条件 空的情况
if (left === null && right !== null) return false
else if (left !== null && right === null) return false
else if (left === null && right === null) return true
else if (left.val !== right.val) return false
// 3. 确定单层递归逻辑
let outSide = compare(left.left, right.right)
let inSide = compare(left.right, right.left)
return outSide && inSide
}
if (!root) return true
return compare(root.left, root.right)
}
迭代法
// 迭代方法判断是否是对称二叉树
// 首先判断root是否为空
var isSymmetric = function (root) {
if (!root) return true
let queue = []
//把左右两个子树要比较的元素顺序放进队列中,然后成对成对的取出来进行比较
queue.push(root.left)
queue.push(root.right)
while (queue.length) {
let leftNode = queue.shift()
let rightNode = queue.shift()
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
}
🎯题目总结
递归法和迭代法,递归依然通过递归三部曲来解决了这道题目
迭代法中使用了队列,需要注意的是这不是层序遍历,而且仅仅通过一个容器来成对的存放我们要比较的元素,知道这一本质之后就发现,用队列,用栈,甚至用数组,都是可以的
🎈今日心得
使用递归解题,一定了解采用的是递归中的那种遍历方式;另外迭代法还需要继续加强