echo

任生命穿梭 时间的角落

0%

填充每个节点的下一个右侧节点指针II

117. 填充每个节点的下一个右侧节点指针 II

给定一个二叉树

1
2
3
4
5
6
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL

初始状态下,所有 next 指针都被设置为 NULL

进阶:

  • 你只能使用常量级额外空间。

  • 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。

    示例:

image-20201002091021913

1
2
3
输入:root = [1,2,3,4,5,null,7]
输出:[1,#,2,3,#,4,5,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。

提示:

  • 树中的节点数小于 6000
  • -100 <= node.val <= 100

方法一:层次遍历

我们可以想到层次遍历,将每一层的结点串起来。这样时间复杂度为O(n)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public Node connect(Node root) {
if(root == null){
return null;
}

Queue<Node> q = new LinkedList<>();
q.offer(root);
while(!q.isEmpty()){
int n = q.size();
//保存上一个结点指针
Node last = null;
//将每层结点链接起来
for(int i = 0; i < n; i++){
Node t = q.poll();
if(t.left != null){
q.offer(t.left);
}
if(t.right != null){
q.offer(t.right);
}

if(i != 0){
last.next = t;
}
last = t;
}
}
return root;
}
}
  • 时间复杂度O(n)
  • 空间复杂度O(n)

方法二:使用已建立的 Next 指针

对于第一层的第一个元素,它的第一个孩子结点就是下一层的起始结点,我们使用一个指针nextStart维护这个结点。将当前层的所有结点的子节点链接起来,在遍历到下一层时,将nextStart置为当前层的首结点就可以访问到当前层的所有结点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution {
Node last = null, 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;
}

private void handle(Node p){
//下一层的起始结点为空
if(nextStart == null){
nextStart = p;
}
//将前一个结点连接到当前结点
if(last != null){
last.next = p;
}
last = p;
}
}
  • 时间复杂度O(n)
  • 空间复杂度O(1)