一、二叉树结构基础知识
1、二叉树的定义
二叉树是满足以下任一一个条件的有限的节点集合:1、为空集 2、由一个根节点和两个不相交的 二叉树 构成,分别成为左子树和右子树。
二叉树对应的算法也本质上是递归 的,包括两部分:1、简单的基本情况——最简单的二叉树是空集2、由其他情况还原为简单情况——不是空集的二叉树都可以分为一个节点和两个不相交的二叉树
2、二叉树的存储结构
二叉树通过自定义类TreeNode来存储,一个节点类包含三个部分: 值 val 和 左子树 leftNode 和 右子树 rightNode
认为树的值为整型,则可以通过如下结构存储二叉树
class TreeNode{
int val;
TreeNode leftNode;
TreeNode rightNode;
}
二、二叉树层序遍历
1、深度优先遍历 DFS 和广度优先遍历 BFS
深度优先遍历DFS和广度优先遍历BFS均为树/树/图的搜索方式,能够访问树/树/图中的所有节点。
深度优先遍历DFS——优先移动节点,当对给定节点尝试过每一种可能性之后,才退到前一 节点来尝试下一个位置。 就像一个搜索者尽可能地深入调查未知的地域,直 到遇到死胡同才回头。
广度优先遍历BFS——优先对给定节点的下一个位置进行进行尝试,当对给定节点尝试过每一种可能性之后,才移动到下一个节点。就像一只搜索军队铺展开来覆盖领土,直到覆盖了所有地域,类似水的涟漪,一层一层一圈一圈的搜索。
DFS借助数据结构Stack栈实现,BFS借助数据结构Queue队列实现。
2、树的层序遍历
树的层序遍历就是树的的广度优先遍历——从上到下、从左到右访问树中的节点,每一层的节点都按顺序出现。
进行树的广度优先遍历,需要借助数据结构Queue队列来实现。
3、树的层序遍历模板
// 二叉树的层序遍历( LeetCode 102 ):https://leetcode-cn.com/problems/binary-tree-level-order-traversal
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
// 设置 res 用来保存输出结果
List<List<Integer>> res = new LinkedList<>();
// 边界情况处理
if(root == null) return res;
// 设置一个队列,用来存储二叉树中的元素
Queue<TreeNode> queue = new LinkedList<>();
// 队列添加二叉树的根节点
queue.add(root);
// 遍历队列,直到队列为空,说明访问了二叉树中所有的节点
while(!queue.isEmpty()){
// 用来记录 queue 的长度,即每层节点的个数
int size = queue.size();
// 用来保存每一层节点,保存成功后添加到 res 中
List<Integer> temp = new ArrayList<>();
// 使用 for 循环,将 queue 中的元素添加的 temp 中
for(int i = 0 ; i < size ; i++ ){
// 从 queue 中取出一个节点
TreeNode node = queue.poll();
// 把节点存放到 list 中
temp.add(node.val); //将节点值加入list
// 判断当前节点的左子节点是否有值,如果有,则添加到 queue 中
if(node.left != null)
queue.add(node.left);
// 判断当前节点的右子节点是否有值,如果有,则添加到 queue 中
if(node.right != null)
queue.add(node.right);
}
// 把存放了每一层元素的数组 temp 添加到 res 中
res.add(temp);
}
// 返回 res
return res;
}
}
4、模板的思路步骤
(1)边界情况处理,一般是判断根节点为空时,答案的处理以及返回情况:例如若根节点为空,返回一个空列表。
List<List<Integer>> res = new LinkedList<>();
// 边界情况处理
if(root == null)
return res;
(2)维护一个队列,用于 BFS 过程,初始化队列,加入根节点。
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
(3)使用while循环进行BFS,退出循环的条件是队列为空。获得当前队列的长度qSize,为二叉树该层节点数。初始化子列表temp,用于储存二叉树该层节点的值。循环 qSize 次,遍历该层的节点,令队头的节点 node 出队,将 node 的值加入子列表temp中。
while(!queue.isEmpty()){
// 用来记录 queue 的长度,即每层节点的个数
int qSize = queue.size();
// 用来保存每一层节点,保存成功后添加到 res 中
List<Integer> temp = new ArrayList<>();
// 使用 for 循环,将 queue 中的元素添加的 temp 中
for(int i = 0 ; i < qSize ; i++ ){
// 从 queue 中取出一个节点
TreeNode node = queue.poll();
// 把节点存放到 list 中
temp.add(node.val); //将节点值加入list
......
}
......
}
(4)判断队头结点node的左右子节点是否存在,若node的左节点存在,入队,若node的右节点存在,入队。
// 遍历队列,直到队列为空,说明访问了二叉树中所有的节点
while(!queue.isEmpty()){
// 用来记录 queue 的长度,即每层节点的个数
int size = queue.size();
// 用来保存每一层节点,保存成功后添加到 res 中
List<Integer> temp = new ArrayList<>();
// 使用 for 循环,将 queue 中的元素添加的 temp 中
for(int i = 0 ; i < size ; i++ ){
// 从 queue 中取出一个节点
TreeNode node = queue.poll();
// 把节点存放到 list 中
temp.add(node.val); //将节点值加入list
// 判断当前节点的左子节点是否有值,如果有,则添加到 queue 中
if(node.left != null)
queue.add(node.left);
// 判断当前节点的右子节点是否有值,如果有,则添加到 queue 中
if(node.right != null)
queue.add(node.right);
}
......
}
(5)经过循环qSize次后,将子列表加入答案列表,最后退出while循环,返回答案列表。
public List<List<Integer>> levelOrder(TreeNode root) {
......
// 遍历队列,直到队列为空,说明访问了二叉树中所有的节点
while(!queue.isEmpty()){
// 用来记录 queue 的长度,即每层节点的个数
int size = queue.size();
// 用来保存每一层节点,保存成功后添加到 res 中
List<Integer> temp = new ArrayList<>();
// 使用 for 循环,将 queue 中的元素添加的 temp 中
for(int i = 0 ; i < size ; i++ ){
// 从 queue 中取出一个节点
TreeNode node = queue.poll();
// 把节点存放到 list 中
temp.add(node.val); //将节点值加入list
// 判断当前节点的左子节点是否有值,如果有,则添加到 queue 中
if(node.left != null)
queue.add(node.left);
// 判断当前节点的右子节点是否有值,如果有,则添加到 queue 中
if(node.right != null)
queue.add(node.right);
}
// 把存放了每一层元素的数组 temp 添加到 res 中
res.add(temp);
}
// 返回 res
return res;
}