LeetCode第117题_填充每个节点的下一个右侧节点指针II

LeetCode 第117题:填充每个节点的下一个右侧节点指针 II

题目描述

给定一个二叉树:

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

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

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

难度

中等

题目链接

点击在LeetCode中查看题目

示例

示例 1:

示例1

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

提示

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

解题思路

方法一:层序遍历(BFS)

与第116题类似,我们可以使用层序遍历(广度优先搜索)来解决这个问题。不同的是,这里的二叉树不一定是完美二叉树,可能有节点缺失。

关键点:

  • 使用队列进行层序遍历
  • 对于每一层,依次连接节点的next指针
  • 每层结束时,最后一个节点的next指针设为NULL

具体步骤:

  1. 如果根节点为空,直接返回
  2. 初始化队列,将根节点入队
  3. 当队列不为空时,执行以下操作:
    a. 获取当前层的节点数量
    b. 遍历当前层的所有节点
    c. 对于每个节点,如果它不是当前层的最后一个节点,将其next指针指向队列中的下一个节点
    d. 将当前节点的左右子节点(如果存在)入队
  4. 返回根节点

时间复杂度:O(n),其中n是树中节点的数量,每个节点只会被访问一次
空间复杂度:O(n),队列中最多存储n/2个节点(最后一层的节点数量)

方法二:使用已建立的next指针(常数空间)

虽然这道题中的二叉树不是完美二叉树,但我们仍然可以利用已经建立好的next指针来实现常数空间的解法。

关键点:

  • 使用dummy节点作为每一层的起始节点
  • 使用prev指针在当前层建立next连接
  • 使用curr指针遍历上一层节点

具体步骤:

  1. 如果根节点为空,直接返回
  2. 初始化curr指针指向根节点
  3. 当curr不为空时,执行以下操作:
    a. 初始化dummy节点作为下一层的哑节点,prev指向dummy
    b. 当curr不为空时:
    i. 如果curr的左子节点不为空,将prev的next指针指向curr的左子节点,并将prev移动到curr的左子节点
    ii. 如果curr的右子节点不为空,将prev的next指针指向curr的右子节点,并将prev移动到curr的右子节点
    iii. 将curr指针移动到next节点
    c. 将curr指针移动到下一层的起始节点(即dummy.next)
  4. 返回根节点

时间复杂度:O(n),每个节点只会被访问一次
空间复杂度:O(1),只使用了常数额外空间

图解思路

方法一:层序遍历过程

层数队列状态操作结果
1[1]1.next = NULL1 -> NULL
2[2, 3]2.next = 3, 3.next = NULL2 -> 3 -> NULL
3[4, 5, 7]4.next = 5, 5.next = 7, 7.next = NULL4 -> 5 -> 7 -> NULL

方法二:使用已建立的next指针过程

当前节点操作结果
1dummy.next = 1.left(2), prev = 2, prev.next = 1.right(3), prev = 32 -> 3 -> NULL
2dummy.next = 2.left(4), prev = 4, prev.next = 2.right(5), prev = 54 -> 5
3prev.next = 3.right(7), prev = 74 -> 5 -> 7 -> NULL

代码实现

C# 实现

/*
// Definition for a Node.
public class Node {
    public int val;
    public Node left;
    public Node right;
    public Node next;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, Node _left, Node _right, Node _next) {
        val = _val;
        left = _left;
        right = _right;
        next = _next;
    }
}
*/

public class Solution {
    // 方法一:层序遍历(BFS)
    public Node Connect(Node root) {
        if (root == null) {
            return null;
        }
        
        Queue<Node> queue = new Queue<Node>();
        queue.Enqueue(root);
        
        while (queue.Count > 0) {
            int size = queue.Count;
            
            for (int i = 0; i < size; i++) {
                Node node = queue.Dequeue();
                
                // 如果不是当前层的最后一个节点,设置next指针
                if (i < size - 1) {
                    node.next = queue.Peek();
                }
                
                // 将子节点加入队列
                if (node.left != null) {
                    queue.Enqueue(node.left);
                }
                if (node.right != null) {
                    queue.Enqueue(node.right);
                }
            }
        }
        
        return root;
    }
    
    // 方法二:使用已建立的next指针(常数空间)
    public Node ConnectOptimized(Node root) {
        if (root == null) {
            return null;
        }
        
        Node curr = root;
        
        while (curr != null) {
            // 创建dummy节点作为下一层的起始节点
            Node dummy = new Node(0);
            Node prev = dummy;
            
            // 遍历当前层的节点
            while (curr != null) {
                // 处理左子节点
                if (curr.left != null) {
                    prev.next = curr.left;
                    prev = prev.next;
                }
                
                // 处理右子节点
                if (curr.right != null) {
                    prev.next = curr.right;
                    prev = prev.next;
                }
                
                // 移动到当前层的下一个节点
                curr = curr.next;
            }
            
            // 移动到下一层的起始节点
            curr = dummy.next;
        }
        
        return root;
    }
}

Python 实现

"""
# Definition for a Node.
class Node:
    def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next
"""

class Solution:
    # 方法一:层序遍历(BFS)
    def connect(self, root: 'Node') -> 'Node':
        if not root:
            return None
        
        queue = collections.deque([root])
        
        while queue:
            size = len(queue)
            
            for i in range(size):
                node = queue.popleft()
                
                # 如果不是当前层的最后一个节点,设置next指针
                if i < size - 1:
                    node.next = queue[0]
                
                # 将子节点加入队列
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
        
        return root
    
    # 方法二:使用已建立的next指针(常数空间)
    def connectOptimized(self, root: 'Node') -> 'Node':
        if not root:
            return None
        
        curr = root
        
        while curr:
            # 创建dummy节点作为下一层的起始节点
            dummy = Node(0)
            prev = dummy
            
            # 遍历当前层的节点
            while curr:
                # 处理左子节点
                if curr.left:
                    prev.next = curr.left
                    prev = prev.next
                
                # 处理右子节点
                if curr.right:
                    prev.next = curr.right
                    prev = prev.next
                
                # 移动到当前层的下一个节点
                curr = curr.next
            
            # 移动到下一层的起始节点
            curr = dummy.next
        
        return root

C++ 实现

/*
// 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:
    // 方法一:层序遍历(BFS)
    Node* connect(Node* root) {
        if (root == nullptr) {
            return nullptr;
        }
        
        queue<Node*> q;
        q.push(root);
        
        while (!q.empty()) {
            int size = q.size();
            
            for (int i = 0; i < size; i++) {
                Node* node = q.front();
                q.pop();
                
                // 如果不是当前层的最后一个节点,设置next指针
                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;
    }
    
    // 方法二:使用已建立的next指针(常数空间)
    Node* connectOptimized(Node* root) {
        if (root == nullptr) {
            return nullptr;
        }
        
        Node* curr = root;
        
        while (curr != nullptr) {
            // 创建dummy节点作为下一层的起始节点
            Node* dummy = new Node(0);
            Node* prev = dummy;
            
            // 遍历当前层的节点
            while (curr != nullptr) {
                // 处理左子节点
                if (curr->left != nullptr) {
                    prev->next = curr->left;
                    prev = prev->next;
                }
                
                // 处理右子节点
                if (curr->right != nullptr) {
                    prev->next = curr->right;
                    prev = prev->next;
                }
                
                // 移动到当前层的下一个节点
                curr = curr->next;
            }
            
            // 移动到下一层的起始节点
            curr = dummy->next;
            
            // 释放dummy节点
            delete dummy;
        }
        
        return root;
    }
};

执行结果

C# 实现

  • 执行用时:92 ms
  • 内存消耗:39.8 MB

Python 实现

  • 执行用时:48 ms
  • 内存消耗:16.2 MB

C++ 实现

  • 执行用时:12 ms
  • 内存消耗:17.4 MB

性能对比

语言执行用时内存消耗特点
C#92 ms39.8 MB代码结构清晰,但性能较慢
Python48 ms16.2 MB代码简洁,性能适中
C++12 ms17.4 MB执行速度最快,内存占用适中

代码亮点

  1. 🎯 方法二使用dummy节点巧妙地处理了下一层的起始节点问题
  2. 💡 使用prev指针在当前层建立next连接,避免了使用队列
  3. 🔍 正确处理了节点缺失的情况,适用于任意二叉树
  4. 🎨 两种方法各有优势,BFS代码简单直观,优化方法空间效率高

常见错误分析

  1. 🚫 没有正确处理节点缺失的情况,导致空指针异常
  2. 🚫 在方法二中,没有正确维护prev指针,导致next连接错误
  3. 🚫 忘记更新curr指针到下一层的起始节点
  4. 🚫 在C++实现中,没有释放dummy节点,导致内存泄漏

解法对比

解法时间复杂度空间复杂度优点缺点
层序遍历(BFS)O(n)O(n)实现简单,直观易懂需要额外空间存储队列
使用next指针O(n)O(1)空间效率高实现稍复杂

相关题目

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值