目录
搜索与回溯的碰撞
1 从上到下打印二叉树 ①
利用队列的"先进先出"特性,构建了一个节点队列,一个结果队列,层序遍历
package jzof.Day06;
import Day14.no6._3;
import jzof.TreeNode;
import java.util.LinkedList;
import java.util.Queue;
/**
* @author ahan
* @create_time 2021-11-09-9:12 上午
* 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
*/
public class _34 {
public static void main(String[] args) {
TreeNode root = new TreeNode(3);
TreeNode node1 = new TreeNode(9);
TreeNode node2 = new TreeNode(20);
TreeNode node3 = new TreeNode(15);
TreeNode node4 = new TreeNode(7);
root.left = node1;
root.right = node2;
node2.left = node3;
node2.right = node4;
TreeNode root2 = new TreeNode(1);
TreeNode node12 = new TreeNode(2);
TreeNode node22 = new TreeNode(3);
TreeNode node32 = new TreeNode(4);
TreeNode node42 = new TreeNode(5);
root2.left = node12;
root2.right = node22;
node22.left = node32;
node22.right = node42;
int[] ints = new _34().levelOrder_2(root);
int[] ints2 = new _34().levelOrder_2(root2);
for (int i = 0; i < ints.length; i++) {
System.out.println(ints[i]);
}
for (int i = 0; i < ints2.length; i++) {
System.out.println(ints2[i]);
}
}
public int[] levelOrder(TreeNode root) {
Queue<Integer> res = new LinkedList<>();
if (root != null){
int[] left_res = levelOrder(root.left);
int[] right_res = levelOrder(root.right);
res.offer(root.val);
if (left_res != null){
for (int i = 0; i < left_res.length; i++) {
res.offer(left_res[i]);
}
}
if (right_res != null){
for (int i = 0; i < right_res.length; i++) {
res.offer(right_res[i]);
}
}
int [] t = new int[res.size()];
int i = 0;
while(!res.isEmpty()){
t[i++] = res.poll();
}
return t;
}else{
return new int[]{};
}
}
public int[] levelOrder_1(TreeNode root) {
Queue<Integer> queue = new LinkedList();
getLevelOrder(root, queue);
int [] t = new int[queue.size()];
int i = 0;
while(!queue.isEmpty()){
t[i++] = queue.poll();
}
return t;
}
public void getLevelOrder(TreeNode root, Queue queue){
if (root != null) {
queue.offer(root.val);
getLevelOrder(root.left, queue);
getLevelOrder(root.right, queue);
}
}
// 以上两种在处理一些样例都会出错。
// 直接用层序遍历
public int[] levelOrder_2(TreeNode root) {
Queue<TreeNode> queue = new LinkedList();
Queue<Integer> result = new LinkedList();
if(root == null){
return new int[0];
}
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode poll = queue.poll();
result.offer(poll.val);
if(poll.left != null)
queue.offer(poll.left);
if(poll.right != null)
queue.offer(poll.right);
}
int [] t = new int[result.size()];
int i = 0;
while(!result.isEmpty()){
t[i++] = result.poll();
}
return t;
}
}
一开始想使用递归,但是测试例子有的没通过,,
上面是大佬的流程解析,我把列表res换成了队列res ~
2 从上到下打印二叉树 ②
1. 俩队列
利用两个队列互相存储一层的节点,再利用temp队列存储后转为List,再判断List长度是否不为空,最后存入res List内。
package jzof.Day06;
import jzof.TreeNode;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/**
* @author ahan
* @create_time 2021-11-09-2:24 下午
*/
public class _34_2 {
public static void main(String[] args) {
TreeNode root = new TreeNode(3);
TreeNode node1 = new TreeNode(9);
TreeNode node2 = new TreeNode(20);
TreeNode node3 = new TreeNode(15);
TreeNode node4 = new TreeNode(7);
root.left = node1;
root.right = node2;
node2.left = node3;
node2.right = node4;
List<List<Integer>> lists = new _34_2().levelOrder(root);
System.out.println(lists);
}
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue_1 = new LinkedList();
Queue<TreeNode> queue_2 = new LinkedList();
Queue<TreeNode> temp = new LinkedList();
Queue<Integer> result = new LinkedList();
List<List<Integer>> res = new ArrayList<>();
if(root == null){
return res;
}
queue_1.offer(root);
int i = 0;
while(true){
if(queue_1.isEmpty() && queue_2.isEmpty()){
break;
}
while (!queue_1.isEmpty()) {
TreeNode poll = queue_1.poll();
System.out.println(poll.val+" "+ i);
temp.offer(poll);
if(poll.left != null)
queue_2.offer(poll.left);
if(poll.right != null)
queue_2.offer(poll.right);
}
i++;
List<Integer> t1 = new ArrayList<>();
while(!temp.isEmpty()){
t1.add(temp.poll().val);
}
if (t1.size() != 0)
res.add(t1);
while (!queue_2.isEmpty()) {
TreeNode poll = queue_2.poll();
System.out.println(poll.val+" "+ i);
temp.offer(poll);
if(poll.left != null)
queue_1.offer(poll.left);
if(poll.right != null)
queue_1.offer(poll.right);
}
i++;
List<Integer> t2 = new ArrayList<>();
while(!temp.isEmpty()){
t2.add(temp.poll().val);
}
if (t2.size() != 0)
res.add(t2);
}
return res;
}
}
又把代码进行了精简,优化
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue_1 = new LinkedList();
Queue<TreeNode> queue_2 = new LinkedList();
List<List<Integer>> res = new ArrayList<>();
if(root == null){
return res;
}
queue_1.offer(root);
TreeNode temp ;
while(!queue_1.isEmpty()){
List<Integer> t_list = new ArrayList<>();
while (!queue_1.isEmpty()) {
temp = queue_1.poll();
t_list.add(temp.val);
if(temp.left != null)
queue_2.offer(temp.left);
if(temp.right != null)
queue_2.offer(temp.right);
}
if (t_list.size() != 0)
res.add(t_list);
t_list = new ArrayList<>();
while (!queue_2.isEmpty()) {
temp = queue_2.poll();
t_list.add(temp.val);
if(temp.left != null)
queue_1.offer(temp.left);
if(temp.right != null)
queue_1.offer(temp.right);
}
if (t_list.size() != 0)
res.add(t_list);
}
return res;
}
}
2. 题解 利用一个queue即可
在获取每一层的时候,先用i存一下当前queue.size(),进行for循环,添加到tmp中。
防止队列增长后,导致判断条件的改变~
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null) queue.add(root);
while(!queue.isEmpty()) {
List<Integer> tmp = new ArrayList<>();
for(int i = queue.size(); i > 0; i--) {
TreeNode node = queue.poll();
tmp.add(node.val);
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
res.add(tmp);
}
return res;
}
}
复杂度分析:
- 时间复杂度 O(N) : N 为二叉树的节点数量,即 BFS 需循环 N 次。
- 空间复杂度 O(N) : 最差情况下,即当树为平衡二叉树时,最多有 N/2 个树节点同时在
queue
中,使用 O(N)大小的额外空间。
3 从上到下打印二叉树 ③
和第二题类似,只需要将偶数行存储临时val的队列temp改为一个双端队列Deque来实现栈的功能,存入res List前多一次出栈即可。
1. 双队列思路延续
package jzof.Day06;
import jzof.TreeNode;
import java.util.*;
/**
* @author ahan
* @create_time 2021-11-09-2:24 下午
*/
public class _34_3 {
public static void main(String[] args) {
TreeNode root = new TreeNode(3);
TreeNode node1 = new TreeNode(9);
TreeNode node2 = new TreeNode(20);
TreeNode node3 = new TreeNode(15);
TreeNode node4 = new TreeNode(7);
root.left = node1;
root.right = node2;
node2.left = node3;
node2.right = node4;
List<List<Integer>> lists = new _34_3().levelOrder_1(root);
System.out.println(lists);
}
public List<List<Integer>> levelOrder_1(TreeNode root) {
Queue<TreeNode> queue_1 = new LinkedList();
Queue<TreeNode> queue_2 = new LinkedList();
List<List<Integer>> res = new ArrayList<>();
Deque<Integer> reverse_temp = new LinkedList<>();
if(root == null){
return res;
}
queue_1.offer(root);
TreeNode temp ;
// int i = 0;
while(!queue_1.isEmpty()){
List<Integer> t_list = new ArrayList<>();
while (!queue_1.isEmpty()) {
temp = queue_1.poll();
t_list.add(temp.val);
// System.out.println(temp.val+" "+ i);
if(temp.left != null)
queue_2.offer(temp.left);
if(temp.right != null)
queue_2.offer(temp.right);
}
// i++;
if (t_list.size() != 0)
res.add(t_list);
t_list = new ArrayList<>();
while (!queue_2.isEmpty()) {
temp = queue_2.poll();
reverse_temp.push(temp.val);
// System.out.println(temp.val+" "+ i);
if(temp.left != null)
queue_1.offer(temp.left);
if(temp.right != null)
queue_1.offer(temp.right);
}
// i++;
while (!reverse_temp.isEmpty()){
t_list.add(reverse_temp.pop());
}
if (t_list.size() != 0)
res.add(t_list);
}
return res;
}
}
下面是大佬的三种解法,思路清晰,整理学习一下!
2. 层序遍历 + 双端队列
- 利用双端队列的两端皆可添加元素的特性,设打印列表(双端队列)
tmp
,并规定:- 奇数层 则添加至
tmp
尾部 , - 偶数层 则添加至
tmp
头部 。
- 奇数层 则添加至
算法流程:
- 特例处理: 当树的根节点为空,则直接返回空列表
[]
; - 初始化: 打印结果空列表
res
,包含根节点的双端队列deque
; - BFS 循环: 当
deque
为空时跳出;- 新建列表
tmp
,用于临时存储当前层打印结果; - 当前层打印循环: 循环次数为当前层节点数(即
deque
长度);- 出队: 队首元素出队,记为
node
; - 打印: 若为奇数层,将
node.val
添加至tmp
尾部;否则,添加至tmp
头部; - 添加子节点: 若
node
的左(右)子节点不为空,则加入deque
;
- 出队: 队首元素出队,记为
- 将当前层结果
tmp
转化为 list 并添加入res
;
- 新建列表
- 返回值: 返回打印结果列表
res
即可;
public List<List<Integer>> levelOrder_2(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null) queue.add(root);
while(!queue.isEmpty()) {
// List<Integer> tmp = new ArrayList<>();
LinkedList<Integer> tmp = new LinkedList();
for(int i = queue.size(); i > 0; i--) {
TreeNode node = queue.poll();
// tmp.add(node.val);
if(res.size() %2 == 0) tmp.addLast(node.val);
else tmp.addFirst(node.val);
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
res.add(tmp);
}
return res;
}
3. 层序遍历 + 双端队列(奇偶层逻辑分离)
- 方法一代码简短、容易实现;但需要判断每个节点的所在层奇偶性,即冗余了 N 次判断。
- 通过将奇偶层逻辑拆分,可以消除冗余的判断。
算法流程:
与方法一对比,仅 BFS 循环不同。
- BFS 循环: 循环打印奇 / 偶数层,当
deque
为空时跳出;- 打印奇数层: 从左向右 打印,先左后右 加入下层节点;
- 若
deque
为空,说明向下无偶数层,则跳出; - 打印偶数层: 从右向左 打印,先右后左 加入下层节点;
复杂度分析:
- 时间复杂度 O(N)O(N) : 同方法一。
- 空间复杂度 O(N)O(N) : 同方法一。
和我之前的做法有点像,更简洁!
首先 使用的是deque 不需要额外添加一个专门用来反序。
其次在每一行的逻辑处理使用的是 for(int i = queue.size();i>0;i--) 代码上更易读。
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Deque<TreeNode> deque = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null) deque.add(root);
while(!deque.isEmpty()) {
// 打印奇数层
List<Integer> tmp = new ArrayList<>();
for(int i = deque.size(); i > 0; i--) {
// 从左向右打印
TreeNode node = deque.removeFirst();
tmp.add(node.val);
// 先左后右加入下层节点
if(node.left != null) deque.addLast(node.left);
if(node.right != null) deque.addLast(node.right);
}
res.add(tmp);
if(deque.isEmpty()) break; // 若为空则提前跳出
// 打印偶数层
tmp = new ArrayList<>();
for(int i = deque.size(); i > 0; i--) {
// 从右向左打印
TreeNode node = deque.removeLast();
tmp.add(node.val);
// 先右后左加入下层节点
if(node.right != null) deque.addFirst(node.right);
if(node.left != null) deque.addFirst(node.left);
}
res.add(tmp);
}
return res;
}
}
4. 层序遍历 + 倒序
- 此方法的优点是只用列表即可,无需其他数据结构。
- 偶数层倒序: 若
res
的长度为 奇数 ,说明当前是偶数层,则对tmp
执行 倒序 操作。
复杂度分析:
- 时间复杂度 O(N): N 为二叉树的节点数量,即 BFS 需循环 N 次,占用 O(N) 。共完成 少于 N 个节点的倒序操作,占用 O(N) 。
- 空间复杂度 O(N) : 最差情况下,即当树为满二叉树时,最多有 N/2 个树节点同时在
queue
中,使用 O(N) 大小的额外空间。
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null) queue.add(root);
while(!queue.isEmpty()) {
List<Integer> tmp = new ArrayList<>();
for(int i = queue.size(); i > 0; i--) {
TreeNode node = queue.poll();
tmp.add(node.val);
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
if(res.size() % 2 == 1) Collections.reverse(tmp);
res.add(tmp);
}
return res;
}
Collections.reverse() 倒序