116. 填充每个节点的下一个右侧节点指针
文章目录
- [116. 填充每个节点的下一个右侧节点指针](https://leetcode.cn/problems/populating-next-right-pointers-in-each-node/)
- 一、题目
- 二、题解
- 方法一:迭代
- 方法二:递归
一、题目
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL
。
初始状态下,所有 next 指针都被设置为 NULL
。
示例 1:
输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,'#' 标志着每一层的结束。
示例 2:
输入:root = []
输出:[]
提示:
- 树中节点的数量在
[0, 212 - 1]
范围内 -1000 <= node.val <= 1000
进阶:
- 你只能使用常量级额外空间。
- 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
二、题解
方法一:迭代
算法思路:
本题需要将一棵完美二叉树的所有节点用next指针连接起来。解题思路是利用bfs层序遍历的方法。
具体实现:
- 利用队列deq进行层序遍历
- 每一层遍历时:
2.1 保存该层队列大小size
2.2 定义prev指针指向上一节点
2.3 遍历队列中所有节点,将prev->next 指向当前节点
2.4 更新prev为当前节点
2.5 将左右孩子加入队列 - 返回根节点
通过在普通bfs过程中新增prev指针,记录上一节点,就可以正确连接同一层的节点。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() : val(0), left(NULL), right(NULL), next(NULL) {}
Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/
class Solution {
public:
Node* connect(Node* root) {
deque<Node*> deq;
if(root == nullptr) return root;
deq.push_back(root);
while(!deq.empty()){
int size = deq.size();
Node *prev = nullptr;
for(int i = 0; i < size; i++){
Node *cur = deq.front();
deq.pop_front();
//用指针连接节点
if(prev != nullptr) prev->next = cur;
prev = cur;
//加入左右孩子
if(cur->left) deq.push_back(cur->left);
if(cur->right) deq.push_back(cur->right);
}
}
return root;
}
};
算法分析:
时间复杂度 O(N): 遍历全部节点
空间复杂度 O(N): 队列最多存储全部节点
方法二:递归
题目要求我们填充每个节点的next
指针,使其指向右侧节点。这道题是一个二叉树问题,我们可以采用递归的方式来解决。
算法思路:
- 首先,我们可以观察给定的二叉树。由于是一个完美二叉树,每个父节点都有两个子节点,并且所有叶子节点都在同一层。这意味着我们可以通过父节点来连接子节点的
next
指针。具体来说,每个节点的左子节点的next
应该指向右子节点,而每个节点的右子节点的next
应该指向其父节点的next
节点的左子节点(如果感觉抽象可以仔细看看题干里给出的图)。 - 我们可以采用递归的方式进行连接。在递归的过程中,我们要确保每个节点的左子节点的
next
指向右子节点,并且每个节点的右子节点的next
指向其父节点的next
节点的左子节点。这样,我们可以在遍历整棵树的过程中正确地设置next
指针。
具体实现:
- 我们可以定义一个递归函数
connect
,其输入参数为当前节点root
。 - 在函数内部,我们首先进行判断,如果
root
为空节点,直接返回root
。 - 如果
root
不为空,我们要进行连接操作。首先判断root
的左子节点是否存在,若存在,则将root
的左子节点的next
指向root
的右子节点。 - 接着,我们要判断
root
的右子节点是否存在,若存在,则将root
的右子节点的next
指向root
的next
节点的左子节点(即右侧节点)。 - 接下来,我们继续递归地调用
connect
函数,分别传入root
的左子节点和右子节点,以处理这两个子树的连接问题。 - 最后,函数返回
root
节点,递归过程结束。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() : val(0), left(NULL), right(NULL), next(NULL) {}
Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/
class Solution {
public:
Node* connect(Node* root) {
if (root == nullptr) return root;
if (root->left) {
root->left->next = root->right;
if (root->next) {
root->right->next = root->next->left;
}
}
connect(root->left);
connect(root->right);
return root;
}
};
算法分析:
- 时间复杂度:假设树中节点的数量为N,由于我们需要遍历每个节点且每个节点的操作都是常数时间的,因此时间复杂度为O(N)。
- 空间复杂度:由于我们使用了递归来进行遍历,递归调用会占用一定的栈空间。在最坏情况下,二叉树的高度为log(N),因此空间复杂度为O(logN)。由于题目要求不算递归程序的栈空间作为额外空间复杂度,所以这个解法是符合要求的。