给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
进阶:
你只能使用常量级额外空间。
使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
解题思路:
关键的地方就是左子树怎么连接到右子树;我刚开始只想到层次遍历的话,我可以直接一直连到最后,直接保证了同一层的左子树和右子树连在一起,这样会简单很多。但是需要额外辅助空间:O(n);并且,我还需要判断这一层的末尾,刚开始我没想到好的办法,因为这是一个完全二叉树,那么每一层都是2的n次方,因此,只要每一次遍历计数,只要够数了,就指向空;
class Solution {
public:
Node* connect(Node* root) {
if(!root)
return NULL;
queue<Node*> node;
queue<Node*> level;
node.push(root);
while(!node.empty())
{
Node* temp = node.front();
node.pop();
level.push(temp);
if(temp->left)
node.push(temp->left);
if(temp->right)
node.push(temp->right);
}
Node * top =level.back();
top->next=NULL;
Node* old=level.front();
level.pop();
int i=0;
int num=1;
while(!level.empty())
{
if(old){
Node* temp =level.front();
if(num==pow(2,i))
{
i++;
old->next=NULL;
num=0;
old=NULL;
continue;
}
old->next=temp;
}
old = level.front();
num++;
level.pop();
}
return root;
}
};
上面这样做的话,辅助空间就是比O(n)还O(n);看了别人的解答,觉得很好:
这里对于每一层都是有一个size的,那么直接在开始的时候记录这个size,每次只遍历这个size的尺寸即可;从而就是i = 0;i < size - 1;假设一层只有4个,那么只要互相连接前3个即可,最后一个就是空,不需要care
class Solution {
public:
Node* connect(Node* root) {
if (root == nullptr) {
return root;
}
// 初始化队列同时将第一层节点加入队列中,即根节点
queue<Node*> Q;
Q.push(root);
// 外层的 while 循环迭代的是层数
while (!Q.empty()) {
// 记录当前队列大小
int size = Q.size();
// 遍历这一层的所有节点
for(int i = 0; i < size; i++) {
// 从队首取出元素
Node* node = Q.front();
Q.pop();
// 连接
if (i < size - 1) {
node->next = Q.front();
}
// 拓展下一层节点
if (node->left != nullptr) {
Q.push(node->left);
}
if (node->right != nullptr) {
Q.push(node->right);
}
}
}
// 返回根节点
return root;
}
};
解答中给了提示,可以使用递归,那么就化成每一次需要做同样的事;可以从图中看到,如果上一层连接了左右,那么在连接一个root的节点的左右节点的时候,接下来对于root右节点需要连接另一颗右子树的左节点,那么就可以判断root有没有连接next节点,如果连接了那么关系就是 root.right.next = root.next.left;因为是递归,那么只要保证上一层root的next肯定是连接的就可以了
class Solution {
public:
Node* connect(Node* root) {
if (root == nullptr) {
return root;
}
// 从根节点开始
Node* leftmost = root;
while (leftmost->left != nullptr) {
// 遍历这一层节点组织成的链表,为下一层的节点更新 next 指针
Node* head = leftmost; //这个记录这一层最左节点很重要
while (head != nullptr) {
// CONNECTION 1
head->left->next = head->right;
// CONNECTION 2
if (head->next != nullptr) {
head->right->next = head->next->left;
}
// 指针向后移动
head = head->next;
}
// 去下一层的最左的节点
leftmost = leftmost->left;
}
return root;
}
};
而递归的解法就是,深度搜索最左节点,每一次都是连接root的左到右;然后递归root的右节点(这次的递归是保证跨着连,并且之前的左右连,已经保证了root的next是连好的),如果root的next不为空,那么就利用这个公式root.right.next = root.next.left
public Node connect(Node root) {
dfs(root, null);
return root;
}
private void dfs(Node curr, Node next) {
if (curr == null)
return;
curr.next = next;
dfs(curr.left, curr.right);
dfs(curr.right, curr.next == null ? null : curr.next.left);
}