Day14-二叉树-leetcode226,101,104,559,111
- 翻转二叉树
-
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
-
思路
-
把每一个节点的左右孩子交换一下就可以了,就可以达到整体翻转的效果
-
前序遍历、后序遍历
-
递归:1. 函数参数及返回值 2. 确定终止条件 3. 处理逻辑
-
前序:中左右,中,处理节点,交换左右孩子节点,
-
层序:层序遍历也可以把每个节点的左右孩子都翻转一遍
// 用层序遍历(BFS)实现二叉树翻转
var invertTree = function(root) {
// 用队列 queue 辅助层序遍历,把根节点先加入队列。
let queue = []
queue.push(root)
// 如果根节点是 null,直接返回 null。
if (root === null) {
return null
}
// 只要队列不为空,就说明还有节点要处理。
// len = queue.length,记录当前层节点数。
// 用 for 循环遍历当前层的所有节点,每次弹出队首节点。
while(queue.length !== 0) {
let len = queue.length
for (let i = 0; i< len; i++) {
let node = queue.shift()
// 交换当前节点的左右孩子,实现翻转。
// 用临时变量保存左右孩子,然后互换。
let tempLeft = node.left
let tempRight = node.right
node.left = tempRight
node.right = tempLeft
// 如果当前节点有左孩子,加入队列;有右孩子,也加入队列。
// 这样下一轮 while 循环时会处理下一层的节点。
node.left && queue.push(node.left)
node.right && queue.push(node.right)
}
}
// 最后返回翻转后的根节点。
return root
};
// 递归版本的前序遍历
// 递归地交换每个节点的左右孩子即可实现二叉树翻转。
// 关键点:递归时要保存原右孩子,否则会被覆盖。
var invertTree = function(root) {
// 递归终止条件:如果当前节点为空,直接返回 null。
if (!root) {
return null
}
// 暂存当前节点的右孩子,防止后面被覆盖。
const rightNode = root.right
// 递归翻转左子树,并把结果赋值给当前节点的右孩子。
root.right = invertTree(root.left)
// 递归翻转原来的右子树,并赋值给当前节点的左孩子
root.left = invertTree(rightNode)
// 返回翻转后的当前节点。
return root
}
- 对称二叉树
-
给你一个二叉树的根节点 root , 检查它是否轴对称。
-
思路
-
二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的
-
后序遍历:需要收集孩子的信息,并向上一级返回;
-
分为以下几种情况
-
- 左节点为空,右节点不为空,不对称,return false
-
- 左不为空,右为空,不对称 return false
-
- 左右都为空,对称,返回true
-
- 左右都不为空,比较节点数值,不相同就return false
-
- 左右节点都不为空,且数值相同的情况。
-
-
单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
// 递归判断是否为对称二叉树
var isSymmetric = function(root) {
// 使用递归遍历左右子树 递归三部曲
// 1. 确定递归的参数 root.left root.right 返回值true false
const compareNode = function(left, right) {
// 2. 确定终止条件 空的情况
if (left === null && right !== null || 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 = compareNode(left.left, right.right)
let inSide = compareNode(left.right, right.left)
return outSide && inSide
}
if (root === null) {
return true
}
return compareNode(root.left, root.right)
}
- 二叉树的最大深度
-
给定一个二叉树 root ,返回其最大深度。
-
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
-
思路
-
高度:从下往上数,后序遍历,左右中,中间的处理过程返回给父节点,父节点再做一个加一,就知道自己的高度了
-
深度:从上往下数,前序,中左右,中是处理过程,往下遍历一个就加一,
-
根节点的高度就是二叉树的最大深度,所以用后序遍历也可以通过,
-
递归:可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。1. 可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。2. 确定终止条件:如果为空节点的话,就返回0,表示高度为0。 3. 确定单层递归的逻辑:先求它的左子树的深度,再求右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
-
迭代:因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度。
// 递归 左右中
var maxdepth = function(root) {
if (root === null) return 0
// 左子树深度
let leftDepth = maxdepth(root.left)
// 右子树深度
let rightDepth = maxdepth(root.right)
// 二叉树的深度需要左右子树深度的最大值加上根节点这一层1
let depth = 1 + Math.max(leftDepth, rightDepth)
return depth
}
var maxdepth = function(root) {
// 使用递归的方法,递归三部曲
// 1. 确定递归函数的参数和返回值
const getDepth = function(node) {
// 2. 确定终止条件
if (node === null) {
return 0
}
// 3. 确定单层逻辑
let leftDepth = getDepth(node.left)
let rightDepth = getDepth(node.right)
let depth = 1 + Math.max(leftDepth, rightDepth)
return depth
}
return getDepth(root)
}
// 二叉树最大深度层级遍历
var maxDepth = function(root) {
if(!root) return 0
let count = 0
const queue = [root]
while(queue.length) {
let size = queue.length
// 层数+1
count++
while(size--) {
let node = queue.shift()
node.left && queue.push(node.left)
node.right && queue.push(node.right)
}
}
return count
}
- n叉树的最大深度
- 给定一个 n 叉树,找到其最大深度。
- 最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。
// 递归
var maxDepth = function(root) {
if(!root) return 0
let depth = 0
for(let node of root.children) {
depth = Math.max(depth, maxDepth(node))
}
return depth + 1
}
// 层序遍历
var maxDepth = function(root) {
if (!root) return 0
let count = 0;
let queue = [root]
while(queue.length) {
let len = queue.length
count++
while(len--) {
let node = queue.shift()
for(let item of node.children) {
item && queue.push(item)
}
}
}
return count
}
- 二叉树的最小深度
-
给定一个二叉树,找出其最小深度。
-
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
-
说明: 叶子节点是指没有子节点的节点。
-
思路
-
注意:叶子节点,左右孩子都为空的节点才是叶子节点!
-
递归法:1. 确定递归函数的参数和返回值,参数为要传入的二叉树根节点,返回的是深度;2. 确定终止条件,遇到空节点返回0,表示当前节点的高度为0;3. 确定单层递归的逻辑,左子树为空,右子树不空,则最小深度为1+右子树的最小深度,,右子树为空,左子树不为空,最小深度是1+左子树最小深度,,左右子树都不为空,返回左右子树最小深度的较小值+1
-
迭代:层序,只有当左右孩子都为空的时候,才说明遍历到最低点了。如果其中一个孩子不为空则不是最低点
var minDepth = function(root) {
if (!root) return 0
// 到叶子节点 返回1
if(!root.left && !root.right) return 1
// 只有右节点时,递归右节点
if(!root.left) return 1 + minDepth(root.right)
// 只有左节点时,递归做节点
if(!root.right) return 1 + minDepth(root.left)
return Math.min(minDepth(root.left), minDepth(root.right)) + 1
}
// 迭代法,层序
var minDepth = function(root) {
if(!root) return 0;
const queue = [root];
let dep = 0;
while(true) {
let size = queue.length;
dep++;
while(size--){
const node = queue.shift();
// 到第一个叶子节点 返回 当前深度
if(!node.left && !node.right) return dep;
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
}
};