一. 什么是二叉树
二叉树是指满足以下要求的树:
- 它可以没有根结点,作为一棵空树存在
- 如果它不是空树,那么必须由根结点、左子树和右子树组成,且左右子树都是二叉树
注意,二叉树不能被简单定义为每个结点的度都是2的树。普通的树并不会区分左子树和右子树,但在二叉树中,左右子树的位置是严格约定、不能交换的。对应到图上来看,也就意味着 B 和 C、D 和 E、F 和 G 是不能互换的。
二. 二叉树的递归遍历
先序遍历
根结点 -> 左子树 -> 右子树
js
// 所有遍历函数的入参都是树的根结点对象
function preorder(root) {
// 递归边界,root 为空
if(!root) {
return
}
// 输出当前遍历的结点值
console.log('当前遍历的结点值是:', root.val)
// 递归遍历左子树
preorder(root.left)
// 递归遍历右子树
preorder(root.right)
}
中序遍历
左子树 -> 根结点 -> 右子树
js
// 所有遍历函数的入参都是树的根结点对象
function inorder(root) {
// 递归边界,root 为空
if(!root) {
return
}
// 递归遍历左子树
inorder(root.left)
// 输出当前遍历的结点值
console.log('当前遍历的结点值是:', root.val)
// 递归遍历右子树
inorder(root.right)
}
后序遍历
左子树 -> 右子树 -> 根结点
scss
function postorder(root) {
// 递归边界,root 为空
if(!root) {
return
}
// 递归遍历左子树
postorder(root.left)
// 递归遍历右子树
postorder(root.right)
// 输出当前遍历的结点值
console.log('当前遍历的结点值是:', root.val)
}
三. 题目
144. 二叉树的前序遍历
1. 题目
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
示例 1:
ini
输入:root = [1,null,2,3]
输出:[1,2,3]
示例 2:
ini
输入:root = []
输出:[]
示例 3:
ini
输入:root = [1]
输出:[1]
示例 4:
ini
输入:root = [1,2]
输出:[1,2]
示例 5:
ini
输入:root = [1,null,2]
输出:[1,2]
提示:
- 树中节点数目在范围 [0, 100] 内
- -100 <= Node.val <= 100
2. 解答
js
/**
* 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 {number[]}
*/
var preorderTraversal = function(root) {
const res = []
if(!root){
return res
}
const stack = []
stack.push(root)
while(stack.length){
const cur = stack.pop()
res.push(cur.val)
if(cur.right){
stack.push(cur.right)
}
if(cur.left){
stack.push(cur.left)
}
}
return res
};
js
/**
* 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 {number[]}
*/
var preorder = function(root, res) {
if(root === null) return
res.push(root.val)
preorder(root.left, res)
preorder(root.right, res)
return
}
var preorderTraversal = function(root) {
let res = []
preorder(root, res)
return res
};
589. N 叉树的前序遍历
1. 题目
给定一个 n 叉树的根节点 root ,返回 其节点值的 前序遍历 。
n 叉树 在输入中按层序遍历进行序列化表示,每组子节点由空值 null 分隔(请参见示例)。
示例 1:
ini
输入:root = [1,null,3,2,4,null,5,6]
输出:[1,3,5,6,2,4]
示例 2:
csharp
输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
输出:[1,2,3,6,7,11,14,4,8,12,5,9,13,10]
提示:
- 节点总数在范围 [0, 104]内
- 0 <= Node.val <= 104
- n 叉树的高度小于或等于 1000
2. 解答
js
/**
* // Definition for a Node.
* function Node(val, children) {
* this.val = val;
* this.children = children;
* };
*/
/**
* @param {Node|null} root
* @return {number[]}
*/
var _preorder = function(root, res) {
if(root === null) return
res.push(root.val)
for(let i of root.children) {
_preorder(i, res)
}
return
}
var preorder = function(root) {
let res = []
_preorder(root, res)
return res
};
226. 翻转二叉树
1. 题目
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
示例 1:
ini
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
示例 2:
ini
输入:root = [2,1,3]
输出:[2,3,1]
示例 3:
输入:root = []
输出:[]
提示:
- 树中节点数目范围在 [0, 100] 内
- -100 <= Node.val <= 100
2. 解答
js
/**
* 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 {TreeNode}
*/
var invertTree = function(root) {
if(!root) return root
let left = invertTree(root.left)
let right = invertTree(root.right)
root.left = right
root.right = left
return root
};
剑指 Offer 32 - II. 从上到下打印二叉树 II
1. 题目
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
例如:
给定二叉树:[3,9,20,null,null,15,7]
,
markdown
3
/ \
9 20
/ \
15 7
返回其层次遍历结果:
csharp
[
[3],
[9,20],
[15,7]
]
提示:
- 节点总数 <= 1000
2. 解答
js
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {number[][]}
*/
var levelOrder = function(root) {
let res = []
getResult(root, 0, res)
return res
};
var getResult = function(root, k, res) {
if(root === null) return
if(k === res.length) res.push([])
res[k].push(root.val)
getResult(root.left, k + 1, res)
getResult(root.right, k + 1, res)
return
}
107. 二叉树的层序遍历 II
1. 题目
给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
示例 1:
ini
输入:root = [3,9,20,null,null,15,7]
输出:[[15,7],[9,20],[3]]
示例 2:
ini
输入:root = [1]
输出:[[1]]
示例 3:
输入:root = []
输出:[]
提示:
- 树中节点数目在范围 [0, 2000] 内
- -1000 <= Node.val <= 1000
2. 解答
js
/**
* 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 {number[][]}
*/
var levelOrderBottom = function(root) {
let res = []
let k = 0
lv(root, k, res)
// 交换位置
for(let i = 0, j = res.length - 1; i < j; i++, j--) {
[res[i], res[j]] = [res[j], res[i]]
}
return res
};
var lv = function(root, k, res) {
if(root === null) return
if(k === res.length) res.push([])
res[k].push(root.val)
lv(root.left, k + 1, res)
lv(root.right, k + 1, res)
return
}
103. 二叉树的锯齿形层序遍历
1. 题目
给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
示例 1:
ini
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[20,9],[15,7]]
示例 2:
ini
输入:root = [1]
输出:[[1]]
示例 3:
输入:root = []
输出:[]
提示:
- 树中节点数目在范围 [0, 2000] 内
- -100 <= Node.val <= 100
2. 解答
js
/**
* 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 {number[][]}
*/
var zigzagLevelOrder = function(root) {
let res = []
let k = 0
getResult(root, k, res)
for(let i = 1; i < res.length; i+=2) {
rever(res[i])
}
return res
};
var rever = function(ary) {
for(let i = 0, j = ary.length - 1; i < j; i++,j--) {
[ary[i], ary[j]] = [ary[j], ary[i]]
}
return ary
}
var getResult = function(root, k, res) {
if(root === null) return
if(k === res.length) res.push([])
res[k].push(root.val)
getResult(root.left, k + 1, res)
getResult(root.right, k + 1, res)
return
}
js
/**
* 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 {number[][]}
*/
var zigzagLevelOrder = function(root) {
let res = []
let k = 0
getResult(root, k, res)
return res
};
var getResult = function(root, k, res) {
if(root === null) return
if(k === res.length) res.push([])
k%2 ? res[k].unshift(root.val) : res[k].push(root.val)
getResult(root.left, k + 1, res)
getResult(root.right, k + 1, res)
return
}
110. 平衡二叉树
1. 题目
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例 1:
csharp
输入:root = [3,9,20,null,null,15,7]
输出:true
示例 2:
csharp
输入:root = [1,2,2,3,3,null,null,4,4]
输出:false
示例 3:
ini
输入:root = []
输出:true
提示:
- 树中的节点数在范围 [0, 5000] 内
- -104 <= Node.val <= 104
2. 解答
js
/**
* 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) {
return getHeight(root) >= 0
};
var getHeight = function(root){
if(root===null) return 0
let l = getHeight(root.left)
let r = getHeight(root.right)
if(l < 0 || r < 0) return -2
if(Math.abs(l - r) > 1) return -2
return Math.max(l, r) + 1
}
/**
* 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) {
let flag = true
function dfs(root){
if(!root || !flag){
return 0
}
let left = dfs(root.left)
let right = dfs(root.right)
if(Math.abs(left - right) > 1){
flag = false
return 0
}
return Math.max(left, right) + 1
}
dfs(root)
return flag
};
112. 路径总和
1. 题目
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。
示例 1:
csharp
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。
示例 2:
ini
输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。
示例 3:
ini
输入:root = [], targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径。
提示:
- 树中节点的数目在范围 [0, 5000] 内
- -1000 <= Node.val <= 1000
- -1000 <= targetSum <= 1000
2. 解答
js
/**
* 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} targetSum
* @return {boolean}
*/
var hasPathSum = function(root, targetSum) {
if(root === null) return false
if(root.left === null && root.right === null) return root.val === targetSum
if(root.left && hasPathSum(root.left, targetSum - root.val)) return true
if(root.right && hasPathSum(root.right, targetSum - root.val)) return true
return false
};
js
/**
* 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} targetSum
* @return {boolean}
*/
var hasPathSum = function(root, targetSum) {
if(root === null) return false
if(!root.left && !root.right) return targetSum === root.val
targetSum -= root.val
return hasPathSum(root.left, targetSum) || hasPathSum(root.right, targetSum)
};
105. 从前序与中序遍历序列构造二叉树
1. 题目
给定两个整数数组 preorder
和 inorder
,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
ini
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
示例 2:
ini
输入: preorder = [-1], inorder = [-1]
输出: [-1]
提示:
- 1 <= preorder.length <= 3000
- inorder.length == preorder.length
- -3000 <= preorder[i], inorder[i] <= 3000
- preorder 和 inorder 均 无重复 元素
- inorder 均出现在 preorder
- preorder 保证 为二叉树的前序遍历序列
- inorder 保证 为二叉树的中序遍历序列
2. 解答
js
/**
* 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 {number[]} preorder
* @param {number[]} inorder
* @return {TreeNode}
*/
var buildTree = function(preorder, inorder) {
if(preorder.length === 0) return null
let head = preorder[0]
let index = 0
while(inorder[index] !== head) ++index
let l_pre = [], l_in = [], r_pre = [], r_in = [];
for(let i = 0; i < index; i++) {
l_pre.push(preorder[i+1])
l_in.push(inorder[i])
}
for(let j = index+1; j < preorder.length; j++) {
r_pre.push(preorder[j])
r_in.push(inorder[j])
}
let root = new TreeNode(head)
root.left = buildTree(l_pre, l_in)
root.right = buildTree(r_pre, r_in)
return root
};
106. 从中序与后序遍历序列构造二叉树
1. 题目
给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
示例 1:
css
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
示例 2:
css
输入:inorder = [-1], postorder = [-1]
输出:[-1]
提示:
- 1 <= inorder.length <= 3000
- postorder.length == inorder.length
- -3000 <= inorder[i], postorder[i] <= 3000
- inorder 和 postorder 都由 不同 的值组成
- postorder 中每一个值都在 inorder 中
- inorder 保证是树的中序遍历
- postorder 保证是树的后序遍历
2. 解答
js
/**
* 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 {number[]} inorder
* @param {number[]} postorder
* @return {TreeNode}
*/
var buildTree = function(inorder, postorder) {
if(postorder.length === 0) return null
let head = postorder[postorder.length - 1]
let index= 0
while(inorder[index] !== head) ++index
let l_in = [], l_pos = [], r_in = [],r_pos = [];
for(let i = 0; i < index; i++) {
l_in.push(inorder[i])
l_pos.push(postorder[i])
}
for(let j = index; j < postorder.length - 1; j++) {
r_in.push(inorder[j+1])
r_pos.push(postorder[j])
}
let head_node = new TreeNode(head)
head_node.left = buildTree(l_in, l_pos)
head_node.right = buildTree(r_in, r_pos)
return head_node
};
222. 完全二叉树的节点个数
1. 题目
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
示例 1:
ini
输入:root = [1,2,3,4,5,6]
输出:6
示例 2:
ini
输入:root = []
输出:0
示例 3:
ini
输入:root = [1]
输出:1
提示:
- 树中节点的数目范围是[0, 5 * 104]
- 0 <= Node.val <= 5 * 104
- 题目数据保证输入的树是 完全二叉树
2. 解答
js
/**
* 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 {number}
*/
var countNodes = function(root) {
if(root === null) return 0
return countNodes(root.left) + countNodes(root.right) + 1
};
剑指 Offer 54. 二叉搜索树的第k大节点
1. 题目
给定一棵二叉搜索树,请找出其中第 k 大的节点的值。
示例 1:
ini
输入: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
2
输出: 4
示例 2:
csharp
输入: root = [5,3,6,2,4,null,null,1], k = 3
5
/ \
3 6
/ \
2 4
/
1
输出: 4
限制:
- 1 ≤ k ≤ 二叉搜索树元素个数
2. 解答
二叉搜索树:右节点大于根节点,左节点小于根节点
js
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @param {number} k
* @return {number}
*/
var getLen = function(root) {
if(root === null) return 0
return getLen(root.left) + getLen(root.right) + 1
}
var kthLargest = function(root, k) {
let r_len = getLen(root.right)
if(k <= r_len) return kthLargest(root.right, k)
if(k === r_len+1) return root.val
return kthLargest(root.left, k - r_len - 1)
};
// 中序遍历取第k个
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @param {number} k
* @return {number}
*/
var in_order = function(root, ans) {
if(root === null) return
in_order(root.left, ans)
ans.push(root.val)
in_order(root.right, ans)
return
}
var kthLargest = function(root, k) {
let ans = []
in_order(root, ans)
return ans[ans.length - k]
};
剑指 Offer 26. 树的子结构
1. 题目
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
例如:
给定的树 A:
css
3
/ \
4 5
/ \
1 2
给定的树 B:
4
/
1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。
示例 1:
css
输入:A = [1,2,3], B = [3,1]
输出:false
示例 2:
css
输入:A = [3,4,5,1,2], B = [4,1]
输出:true
限制:
- 0 <= 节点个数 <= 10000
2. 解答
js
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} A
* @param {TreeNode} B
* @return {boolean}
*/
var isSubStructure = function(A, B) {
if(B===null) return false
if(A===null) return false
if(A.val === B.val && is_match(A, B)) return true
return isSubStructure(A.left, B)|| isSubStructure(A.right, B)
};
// 比较每一层
var is_match = function(a, b) {
if(b === null) return true
if(a === null) return false
if(a.val !== b.val) return false
return is_match(a.left, b.left) && is_match(a.right, b.right)
}
662. 二叉树最大宽度
1. 题目
给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空。
每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。
示例 1:
csharp
输入:
1
/ \
3 2
/ \ \
5 3 9
输出: 4
解释: 最大值出现在树的第 3 层,宽度为 4 (5,3,null,9)。
示例 2:
输入:
1
/
3
/ \
5 3
输出: 2
解释: 最大值出现在树的第 3 层,宽度为 2 (5,3)。
示例 3:
输入:
1
/ \
3 2
/
5
输出: 2
解释: 最大值出现在树的第 2 层,宽度为 2 (3,2)。
示例 4:
csharp
输入:
1
/ \
3 2
/ \
5 9
/ \
6 7
输出: 8
解释: 最大值出现在树的第 4 层,宽度为 8 (6,null,null,null,null,null,null,7)。
注意: 答案在32位有符号整数的表示范围内。
2. 解答
js
/**
* 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 {number}
*/
// 给一整个树做编号,从左到右,从上到下开始
// root 的编号为 1,那么root的左孩子编号就为2,右孩子编号为3
// root.left的index = root的index*2 root.right的index = root的index*2+1
// 然后定义一个变量max来记录快读的最大值,每层的序号减完后和max比较取大的值
// width = Rindex - Lindex+1
var widthOfBinaryTree = function(root) {
if(root === null) return 0
// 定义一个二维数组存储当千层的序号和存入的节点
let que = [[0n, root]]
let max = 1n
while(que.length) {
let width = (que[que.length - 1][0] - que[0][0]) + 1n;
if(width > max) max = width
let temp = []
for(let [index, root] of que) {
root.left && temp.push([index * 2n, root.left])
root.right && temp.push([index * 2n + 1n, root.right])
}
que = temp
}
return Number(max)
};