117. 填充每个节点的下一个右侧节点指针 II
难度 中等
给定一个二叉树
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
进阶:
- 你只能使用常量级额外空间。
- 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
示例:
输入:root = [1,2,3,4,5,null,7]
输出:[1,#,2,3,#,4,5,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化输出按层序遍历顺序(由 next 指针连接),'#' 表示每层的末尾。
提示:
- 树中的节点数小于
6000 -100 <= node.val <= 100
题解
解法一:层序遍历
主要通过广度搜索来实现层序遍历,需要辅助队列记录节点和层次,还需要保存前一个节点和层次。通过判断前一个节点和当前节点是否在同一层,如果是同一层,就连接。
class Solution {
public Node connect(Node root) {
if(root == null){
return root;
}
Queue<Node> nodeQueue = new LinkedList<Node>();//辅助队列,记录节点
Queue<Integer> levelQueue = new LinkedList<Integer>();//辅助队列,记录层次
nodeQueue.offer(root);//节点入队
levelQueue.offer(1);//层次入队
Node preNode = root;//前一个节点
Integer preLevel = 0; //前一个节点层次
while(!nodeQueue.isEmpty()){//队列不为空
Node nowNode = nodeQueue.poll();//节点出队
Integer nowlevel = levelQueue.poll();//层次出队
if(nowlevel == preLevel){//如果是同一层
preNode.next = nowNode;//前一个节点指针指向当前节点
}
if(nowNode.left != null){//左孩子不为空
nodeQueue.offer(nowNode.left);
levelQueue.offer(nowlevel + 1);
}
if(nowNode.right != null){//右孩子不为空
nodeQueue.offer(nowNode.right);
levelQueue.offer(nowlevel + 1);
}
preNode = nowNode;//更新节点
preLevel = nowlevel;//更新层次
}
return root;
}
}
看到官方题解之后,确实秒,不需要记录层次,通过for循环直接把一层的节点连接起来。
class Solution {
public Node connect(Node root) {
if(root == null){
return root;
}
Queue<Node> queue = new LinkedList<Node>();//辅助队列
queue.offer(root);//入队
while(!queue.isEmpty()){//队列不为空
int n = queue.size();//队列大小
Node last = null;//当前链表尾指针
for(int i = 1; i <= n; i++){
Node now = queue.poll();//出队
if(now.left != null){//左节点不为空
queue.offer(now.left);
}
if(now.right != null){//右节点不为空
queue.offer(now.right);
}
if(i != 1){//如果不是当层开始节点,当前节点连接上链表最后
last.next = now;
}
last = now;//更新节点
}
}
return root;
}
}
解法二:优化存储空间
上面方法都需要一个辅助队列进行入队出队,但是我们可以发现,我们节点还有个next指针,在我们遍历当前层的时候,可以连接到下一个节点,然后通过next更新到下个节点,如此反复,这样我们就并不需要队列帮我们记录了。
class Solution {
Node last = null;//当前链表尾指针
Node nextStart = null;//下一层头指针
public Node connect(Node root) {
if(root == null){
return null;
}
Node start = root;
while(start != null){
last = null;//初始化下一层链表尾指针为空
nextStart = null;//初始化下一层头指针为空
for(Node p = start; p != null; p = p.next){//遍历当前层(按着链表遍历)
if(p.left != null){//左孩子不为空
handle(p.left);
}
if(p.right != null){//右孩子不为空
handle(p.right);
}
}
start = nextStart;//指向下一层的头指针
}
return root;
}
//此函数最好带入实例容易理解
//当遍历到第一层的时候,我们为下一层进行链接
//此时初始化last和nextStart为空
//假设左孩子不为空,即进入此方法
// 第一个判断不成立;第二个判断成立,此时找到了下一层第一个节点(头指针);最后还更新了尾指针last
//假设右孩子不为空,即进入此方法
// 第一个判断成立,尾指针不为空,上个节点指向当前节点;第二个判断不成立;最后还更新了尾指针last
//当第二层都链接完之后,退出for循环,执行start = nextStart;指向下一层的头指针
//然后进行while判断,重复执行上面的注释过程
public void handle(Node p){
if(last != null){//如果当前链表不为空,可进行链接
last.next = p;
}
if(nextStart == null){//如果下一层开始节点为空,则这个节点是开始节点(配合的代码 nextStart = null;)
nextStart = p;
}
last = p;//更新尾指针
}
}