二叉树算法—广度搜索算法使用以及变形
2021-07-13 本文已影响0人
小胖学编程
二叉树的广度搜索算法,不仅可以用来遍历二叉树,其算法亦可以变形使用解决其他二叉树问题。
1. 思索
使用迭代来实现广度搜索
- 需要什么数据结构来存储节点信息?
- 迭代的终止条件?
思索下面几个算法题:
102. 二叉树的层序遍历
429. N 叉树的层序遍历
107. 二叉树的层序遍历 II
2. 分析
广度搜索:即在水平维度一层层的去解析二叉树节点。即节点先进先出(队列结构)。
public List<Integer> levelOrder(TreeNode root) {
if (root == null) {
return null;
}
List<Integer> results = new ArrayList<>();
//使用队列来存储
Queue<TreeNode> queue = new ArrayQueue<>();
//根节点入栈
queue.add(root);
while (!queue.isEmpty()) {
//出栈
TreeNode node = queue.poll();
//存储节点的值
results.add(node.val);
//左右节点存入
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
return results;
}
最简单的算法如上所示。
- 终止条件:队列不为空;
- 循环逻辑:队列出栈,将节点的值存储到集合中,并将节点所有子节点放入到队列中。
3. 变形
3.1 层序遍历

看到这个题,就想到了广度搜索算法。
难点:如何将每一层的节点均存储到一个集合中?
开始想到的思路是:每一层末尾节点后加一个标识位表示分层?但是这种思路是不太好的!
那如何实现分层存储?
思路:首先广度搜索需要有一个大循环来遍历树,在大循环内部可不可以使用内层循环来遍历层呢?即大循环开启的是每层的查询,小循环开启的是每层节点的查询?
难点:如何控制内循环的循环次数?
思路:每遍历一个节点,会将节点所以子节点存储到队列中。当遍历完一次,结束内循环时,队列里面存储的均是下一层的节点,而队列的大小就是内循环的次数。
/**
* 广度遍历。
* 大集合套小集合,思路是必须使用两个循环。
* 大循环控制整棵树的遍历次数;
* 小循环控制每一层的遍历次数;
* <p>
* 思路:因为结果集是List<List<Integer>>格式,那么一层循环是肯定无法设置的,需要两个循环
* 1. 外层循环控制树的遍历;
* 2. 内存循环控制层级的遍历;
* <p>
* 外层循环,queue数量为1;
* 内存循环,将root的left和right存入后,
* 外层循环,queue数量为2;
* 内存循环,解析的数量为2节点,在内存循环中将个数全部循环完毕,且存入新的集合中。
* 外层循环...
*/
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> results = new ArrayList<>();
if (root == null) {
return results;
}
//使用队列
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
//层级的节点数量
int size = queue.size();
//每一层的遍历结果
List<Integer> result = new ArrayList<>();
while (size > 0) {
//数据的出栈
TreeNode node = queue.poll();
//两个节点填充
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
result.add(node.val);
//控制内层循环结束
size--;
}
results.add(result);
}
return results;
}
107. 二叉树的层序遍历 II这个提醒,就是在组装results时候,使用的是add(0,result)方法,将结果放在集合的第一个元素。
2.2 对称二叉树

这个题如何使用广度搜索来实现?
说实话我是没有思路的,可能是广度遍历二叉树对我影响太深,以至于广度搜索和核心我到现在都没明白。
这个题的难点在于:如何比较最左和最右两个节点是否相等。
广度搜索,使用队列结构来实现,节点进入队列后,然后取出做相应的操作,最后将其所有的子节点再次放入队列,实现对整棵树的遍历。
而本题的思想是两个节点的比较,那么在循环开始前,将root的左右节点放入到队列中,开启循环后,在队列中取出两个节点进行比较,若两个节点相等,按照顺序,将最左和最右两个节点在次放入到队列中。
每次取出来的两个节点是有序的(最左和最右)
/**
* 解答成功:
* 执行耗时:1 ms,击败了28.54% 的Java用户
* 内存消耗:38 MB,击败了5.11% 的Java用户
* 使用广度
*/
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
//每次比较左右两个节点
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root.left);
queue.add(root.right);
while (!queue.isEmpty()) {
//取出节点
TreeNode leftNode = queue.poll();
TreeNode rightNode = queue.poll();
if (leftNode == null && rightNode == null) {
continue;
}
if (leftNode != null && rightNode == null) {
return false;
}
if (leftNode == null && rightNode != null) {
return false;
}
if (leftNode != null && rightNode != null) {
if (leftNode.val != rightNode.val) {
return false;
}
}
//子节点按照顺序两两组合,放入队列中
queue.add(leftNode.left);
queue.add(rightNode.right);
queue.add(leftNode.right);
queue.add(rightNode.left);
}
//循环结束还成功,那么就是真正匹配
return true;
}