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

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


一、题目

给定一个二叉树:

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

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

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

示例 1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ffjhoy9E-1690343956372)(D:\A_WHJ\Computer Science\typora图片\117_sample.png)]

输入:root = [1,2,3,4,5,null,7]
输出:[1,#,2,3,#,4,5,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化输出按层序遍历顺序(由 next 指针连接),'#' 表示每层的末尾。

示例 2:

输入:root = []
输出:[]

提示:

  • 树中的节点数在范围 [0, 6000]
  • -100 <= Node.val <= 100

进阶:

  • 你只能使用常量级额外空间。
  • 使用递归解题也符合要求,本题中递归程序的隐式栈空间不计入额外空间复杂度。
二、题解
方法一:迭代

算法思路:

我们可以使用广度优先搜索(BFS)的方法来遍历二叉树,并在遍历过程中为每个节点的next指针赋值。我们使用一个双端队列(deque)来辅助进行BFS,将每层的节点依次加入队列中,并使用队列中的节点来建立next指针的连接关系。

具体实现:

以下是算法的具体实现步骤:

  1. 首先,我们创建一个双端队列deq来存储待处理的节点。如果根节点为空,直接返回根节点。
  2. 将根节点添加到deq中。
  3. 使用循环遍历队列deq,在每一轮中,我们处理当前队列的所有节点,这代表一层的节点。为了区分每一层,我们需要在每轮开始时记录当前队列的大小,将其保存为size
  4. 创建一个prev指针,用于跟踪前一个节点,初始时设为nullptr
  5. 在循环中,我们取出队列中的节点,并分别进行如下操作:
    • 如果prev不为空,将prev->next指向当前节点cur,建立连接关系。
    • 将当前节点cur赋值给prev,更新prev指针为当前节点。
    • 如果当前节点的左子节点不为空,将左子节点加入队列。
    • 如果当前节点的右子节点不为空,将右子节点加入队列。
  6. 完成当前层的处理后,继续下一轮循环,处理下一层的节点。
  7. 重复步骤5和步骤6,直到队列deq为空,即所有节点的next指针都连接完成。
  8. 返回根节点。
/*
// 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;
    }
};

算法分析:

  • 时间复杂度:BFS遍历二叉树的时间复杂度是O(N),其中N是二叉树节点的数量。
  • 空间复杂度:由于我们使用了一个双端队列来存储待处理节点,队列中的节点数量最多不会超过一层的节点数量,而树的最大宽度不会超过N/2。因此,队列的空间复杂度是O(N/2),即O(N)。除了队列之外,我们没有使用其他与节点数量相关的额外数据结构,因此总的空间复杂度也是O(N)。
方法二:递归

算法思路:

  1. 首先,我们可以观察到,每个节点的左子节点的next指针都可以很容易地连接到右子节点,因为它们在同一层。
  2. 对于每个节点的右子节点,它的next指针可以通过父节点的next指针连接到父节点的右侧节点的最左子节点,或者跨越到父节点的右侧节点的左子节点的最左子节点。
  3. 通过这种方式,我们可以逐层连接节点的next指针,直到遍历完整个树。

具体实现:

为了实现这一思路,我们使用一个辅助函数findNextRight来查找一个节点的右侧节点。然后在connect函数中,我们先连接每个节点的左右子节点,然后再递归地连接右子树和左子树。

具体步骤如下:

  1. 首先判断根节点是否为空,如果为空直接返回nullptr
  2. 如果根节点有左子节点,我们先尝试将其左子节点连接到右子节点。如果根节点的右子节点存在,我们直接将左子节点的next指针连接到右子节点。否则,我们调用findNextRight函数来查找根节点的右侧节点,然后将左子节点的next指针连接到右侧节点的最左子节点。
  3. 如果根节点有右子节点,我们调用findNextRight函数来查找根节点的右侧节点,然后将右子节点的next指针连接到右侧节点的最左子节点。
  4. 然后我们递归地先连接右子树再连接左子树,保证在连接左子树时右子树已经连接好了。
  5. 最后返回根节点。
class Solution {
public:
    Node* connect(Node* root) {
        if (root == nullptr) return nullptr;

        if (root->left) {
            if (root->right) {
                root->left->next = root->right;
            } else {
                root->left->next = findNextRight(root->next);
            }
        }

        if (root->right) {
            root->right->next = findNextRight(root->next);
        }

        connect(root->right); // 注意先连接右子树再连接左子树
        connect(root->left);

        return root;
    }

private:
    Node* findNextRight(Node* node) {
        while (node) {
            if (node->left) return node->left;
            if (node->right) return node->right;
            node = node->next;
        }
        return nullptr;
    }
};

算法分析:

  • 时间复杂度:假设二叉树中有n个节点,则遍历整个树需要O(n)的时间复杂度。
  • 空间复杂度:空间复杂度取决于树的形状,最好的情况下是O(logn),最坏的情况下是O(n)。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KeepCoding♪Toby♪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值