原题链接:https://leetcode.com/problems/n-ary-tree-level-order-traversal/
1. 题目介绍
Given an n-ary tree, return the level order traversal of its nodes’ values. (ie, from left to right, level by level).
For example, given a 3-ary tree:
给定一个n叉树,返回逐层遍历的结果。
逐层遍历的意思是从上层遍历到下层,在每层中从左边遍历到右边。
举个例子:下面这个三叉树:
我们需要返回它的逐层遍历的顺序是:
[
[1],
[3,2,4],
[5,6]
]
Note:
The depth of the tree is at most 1000.
The total number of nodes is at most 5000.
树的最大深度是1000
节点总数最大是5000
2. 解题思路
这道题是一道求树节点搜索路径的题目,要求一个n叉树的逐层遍历路径。LeetCode中还有一道求二叉树的搜索路径的题目,建议先做完 102. Binary Tree Level Order Traversal 后,再做本题。
方法1 队列 + 标记
如何区分不同的层是本题的一个难点,其中一种方式是使用队列+标记的方法。
每次将一层的所有节点都放入队列之后,就在队列尾部插入一个null节点,用于标记一层的结束。
首先需要设置一个 List< Integer > res 用于存放同一层的节点val值。
a) 当从队列前方取出元素时,如果取出的元素是节点,那么就将该节点的 val 存放入 res ,并且将 children 里面的子节点都放入队列。
b) 当从队列前方取出的元素是 null 时,说明已经遍历完一层了,此时 res 里面的值就是这一层所有节点的 val 值,于是将 res 复制到 temp 里,将 temp 存入总的结果 ans 里。同时 res 还要继续使用,去存下一层的值,因此要将 res 清空。此时的队列中,队列的尾部已经存放好了新的一层的节点了,需要在队列最后面再补一个 null 标记这个新的一层的结束。
实现代码
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> children;
public Node() {}
public Node(int _val,List<Node> _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public List<List<Integer>> levelOrder(Node root) {
List< List<Integer> > ans = new ArrayList<>();
if(root == null){
return ans;
}
List<Integer> res = new ArrayList<>();
Queue<Node> queue = new LinkedList<>();
queue.add(root);
queue.add(null);
while(queue.isEmpty() == false){
Node t = queue.remove();//取出队列最前方的节点
if(t != null){
//如果该节点不为null,则将其val放入res,所有子节点放入队列
res.add(t.val);
for(Node i : t.children){
queue.add(i);
}
}else{
//如果该节点为null,则说明已经遍历完一层
//首先要在队列尾部添加一个新的null,标志这一层结束
//然后需要将当前res的值存入ans,并且同时清空res
if(queue.isEmpty() == false){
queue.add(null);
}
List<Integer> temp = new ArrayList<>(res);
ans.add(temp);
res.clear();
}
}
return ans;
}
}
方法2 队列 + 计数
如果使用队列,还会有一个规律:
每当从队列前部移出去一层以后,队列中剩余节点的个数就是树的下一层的节点数。
因此可以利用这个规律来对树进行分层。
实现代码
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> children;
public Node() {}
public Node(int _val,List<Node> _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> ans = new ArrayList<>();
if(root == null){
return ans;
}
Queue<Node> queue = new LinkedList<>();
queue.add(root);
while(queue.isEmpty() == false){
List<Integer> res = new ArrayList<>();
int n = queue.size();//当前队列长度,也是当前这层的节点个数
int Count = 0;//当前这层的节点中,已经将val值放入res的节点个数
while(Count < n){
//对当前层的每个节点都 1.val值放入res 2.子节点放入队列
Node t = queue.remove();
res.add(t.val);
for(Node i : t.children){
queue.add(i);
}
Count ++;
}
ans.add(res);
}
return ans;
}
}
方法3 深度优先搜索
虽然,逐层遍历通常采用的是广度优先搜索的方法,但是使用深度优先搜索同样可以解决本题。
首先,需要明确一个前提: 总结果List< List < Integer >> ans 的元素个数,就是树的深度。
因此,我们可以使用一个数字 depth 记录节点的深度。比如根节点深度为1,那么它所在的第一层,在 ans 中的下标就是0,正好是depth - 1.于是可以通过depth来完成节点的分层。根据depth,就可以将节点放到正确的层里面。
实现代码
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> children;
public Node() {}
public Node(int _val,List<Node> _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> ans = new ArrayList<>();
if(root == null){
return ans;
}
helper(root, 1 , ans);
return ans;
}
public void helper(Node root , int depth , List<List<Integer>> ans){
if(root == null){
return;
}
//如果树的深度大于ans的元素个数,说明需要新加一层了
if(depth > ans.size()){
List<Integer> res = new ArrayList<>();
ans.add(res);
}
ans.get(depth-1).add(root.val);//深度简易对应ans的下标
//按照深度优先搜索的方式继续遍历下面的子树
for(Node i : root.children){
helper( i, depth+1,ans);
}
}
}