前面的话
队列是一种比较特殊的线性结构,是典型的先进先出。它只允许在表的的前端进行删除,在表的后端进行插入。前面说过,树的深度优先遍历可以借助栈来实现,而树的层次遍历可以借助队列来实现。
队列基础使用
java中linkedList
提供了方法支持队列的使用,实现了Queue接口
。
- 队列的创建:
Queue<Integer> queue = new LinkedList<>();//声明队列
- 添加数据:
offer()、add()
- 删除队首:
poll()
- 查看队首:
peek()
不删除
题目
题目(102)
给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。
例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]
解析
- 根节点入队
- 大循环开始:得到队列的长度(队列的长度为每一层节点的个数)。
- for循环队列的长度,每次取出队首节点。建立一个数组list ,存放节点的val值。检查当前节点有没有左右节点,如果有,左右孩子入队。直到for结束。此时lest数组存储了每一层的val,将list添加进最终的result中。
- 上述大循环直到队列为空,循环结束。
public List<List<Integer>> levelOrder(TreeNode root) {
LinkedList<List<Integer>> res = new LinkedList<>();
if (root == null) {
return res;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root); // 添加根节点
while(!queue.isEmpty()) {
// 创建一个list,用于添加每一层的val
List<Integer> levelList = new ArrayList<Integer>();
int count = queue.size(); // 每一层的长度
for(int i = 0; i < count;i++ ) {
TreeNode node = queue.poll();
levelList.add(node.val);
if(node.left != null) {
queue.offer(node.left);
}
if(node.right != null) {
queue.offer(node.right);
}
}
res.add(levelList);
}
return res;
}
题目 (107)
给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其自底向上的层次遍历为:
[
[15,7],
[9,20],
[3]
]
解析
这一题与上面的题目,只是结果返回的顺序不一样。我们只需要将每次for循环结束后,res
倒序添加levellist
就行。
将res.add(levelList)
改为res.addFrist(levelList)
,倒序添加就行。
题目 (199)
给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
示例:
输入: [1,2,3,null,5,null,4]
输出: [1, 3, 4]
解释:
1 <---
/ \
2 3 <---
\ \
5 4 <---
解析:
这题不一样的地方是,我们只需要将每一层最后一个数添加到res
数组中。
稍作修改:
我们不需要在每一个for 循环中创建一个list来存储每一层的val
值,只需要在每一次for循环结束时,将最后一个节点的val
值添加到结果res
中。
public List<Integer> rightSideView(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) {
return res;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
int count = queue.size(); // 每一层的长度
for(int i = 0; i < count;i++ ) {
TreeNode node = queue.poll();
if(node.left != null) {
queue.offer(node.left);
}
if(node.right != null) {
queue.offer(node.right);
}
if(i== count-1) {
res.add(node.val);
}
}
}
return res;
}
题目 (103)
给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回锯齿形层次遍历如下:
[
[3],
[20,9],
[15,7]
]
解析
方法一:使用队列
这题前面不同的是,偶数层都要倒过来输出。很容易想到,我们设置一个flag
来辨别奇偶行。如果是奇数行,每一个for循环建立的list数组向后添加val
值;如果是偶数行,那么for循环中的list
数组向前添加val
值。
稍作修改:
// 偶数层,从前面添加
if(flag % 2 == 0) {
levelList.add(0, node.val);
}else {
levelList.add(node.val);
}
方法二:双栈交替
- 创建两个栈
stack1
、stack2
(奇偶栈)。分别存储奇偶层的节点 - 根元素入栈
stack1
- 循环取出
stack1
的栈顶节点,并将其val
添加到数组level1
中,如果该节点存在左右节点,将其放入stack2
中。直到stack1
为空,并将level1
添加到res
中。 stack1
为空了,stack2
有节点,循环取出stack2
的栈顶节点,并将其val
添加到数组level2
中,如果该节点存在左右节点,将其放入stack1
中。直到stack2
为空。- 两栈交替。直到两栈都为空,并将
level2
添加到res
中。 - 返回
res
。
// 方法二: 双栈交替
public List<List<Integer>> zigzagLevelOrder2(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if(root == null) {
return res;
}
// 存放奇数层的栈
Stack<TreeNode> stack1 = new Stack<>();
// 存放偶数层的栈
Stack<TreeNode> stack2 = new Stack<>();
stack1.push(root);
while(!stack1.isEmpty() || !stack2.isEmpty()) {
// 两栈交替
if(!stack1.isEmpty()) {
while(!stack1.isEmpty()) {
List<Integer> level1 = new ArrayList<>();
int num1 = stack1.size();
for(int i = 0;i<num1;i++) {
TreeNode node1 = stack1.pop();
level1.add(node1.val);
// 偶数层,先left节点入栈
if(node1.left != null) {
stack2.push(node1.left);
}
if(node1.right != null) {
stack2.push(node1.right);
}
}
res.add(level1);
}
}else {
while(!stack2.isEmpty()) {
// 奇数层,先right节点入栈
List<Integer> level2 = new ArrayList<>();
int num2 = stack2.size();
for(int i = 0;i<num2;i++) {
TreeNode node2 = stack2.pop();
level2.add(node2.val);
if(node2.right != null) {
stack1.push(node2.right);
}
if(node2.left != null) {
stack1.push(node2.left);
}
}
res.add(level2);
}
}
}
return res;
}