面试经典150题[074]:填充每个节点的下一个右侧节点指针 II(LeetCode 117)

填充每个节点的下一个右侧节点指针 II(LeetCode 117)

题目链接:填充每个节点的下一个右侧节点指针 II(LeetCode 117)

难度:中等

1. 题目描述

给你一个二叉树的根节点 root ,请你将其中所有节点的 next 指针指向其右侧的节点。如果没有右侧节点,则 next 指针应设置为 NULL

初始时,所有 next 指针都设置为 NULL

树节点定义:

python 复制代码
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

要求:

  • 二叉树不一定是完美的(即不一定是满二叉树或完全二叉树)。
  • 节点数 0 <= n <= 6000
  • -100 <= Node.val <= 100

示例:

复制代码
输入: root = [1,2,3,4,5,null,7]
输出: [1,#,2,3,#,4,5,7,#]
解释: 给定二叉树如图所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,'#' 标志着每一层的结束。

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

2. 问题分析

2.1 规律

  • 这是一个二叉树问题,需要将同一层的节点通过 next 指针连接起来,形成一个链表。
  • 与 LeetCode 116 不同,本题的树不一定是完美的,因此不能假设每层都有完整的左右子节点。
  • 核心问题:如何在不使用额外空间(如队列)的情况下,高效连接同一层节点?
  • 类似于层序遍历,但需要处理缺失节点的情况。

2.2 贪心思路

我们使用常量空间的层级连接方法:

  • 从根节点开始,逐层处理。
  • 对于每一层,使用一个虚拟节点 dummy 来构建下一层的链表头,并用 tail 指针来连接下一层的子节点。
  • 遍历当前层的节点(通过已连接的 next 指针),为每个节点的左右子节点建立连接:
    • 如果有左子节点,连接到 tail.next
    • 如果有右子节点,连接到 tail.next
  • 当前层遍历结束后,将下一层的头节点设置为 dummy.next,继续处理下一层。
  • 这个方法利用了上一层的 next 指针来遍历当前层,避免使用队列,实现 O(1) 额外空间。
  • 如果树为空或只有根节点,直接返回。

3. 代码实现

Python

python 复制代码
class Solution:
    def connect(self, root: 'Node') -> 'Node':
        if not root:
            return root
        
        head = root
        while head:
            dummy = Node(0)
            tail = dummy
            cur = head
            while cur:
                if cur.left:
                    tail.next = cur.left
                    tail = tail.next
                if cur.right:
                    tail.next = cur.right
                    tail = tail.next
                cur = cur.next
            head = dummy.next
        return root

C++

cpp 复制代码
class Solution {
public:
    Node* connect(Node* root) {
        if (!root) return root;
        
        Node* head = root;
        while (head) {
            Node* dummy = new Node(0);
            Node* tail = dummy;
            Node* cur = head;
            while (cur) {
                if (cur->left) {
                    tail->next = cur->left;
                    tail = tail->next;
                }
                if (cur->right) {
                    tail->next = cur->right;
                    tail = tail->next;
                }
                cur = cur->next;
            }
            head = dummy->next;
            // 注意:在C++中,需要手动删除dummy以避免内存泄漏,但LeetCode环境可忽略
        }
        return root;
    }
};

4. 复杂度分析

  • 时间复杂度:O(n),遍历每个节点一次。
  • 空间复杂度:O(1),只使用常量额外空间(不计递归栈或队列)。

5. 总结

  • 连接 next 指针问题 + 非完美二叉树 → 使用虚拟节点构建下一层链表是首选。
  • 核心维护 dummytail,很通用。
    • 类似于优化后的 BFS 层序遍历,但空间为 O(1)。
    • 可扩展到完美二叉树的变体(LeetCode 116)。

复习

面试经典150题[014]:加油站(LeetCode 134)

面试经典150题[044]:两数之和(LeetCode 1)

面试经典150题[059]:合并两个有序链表(LeetCode 21)

相关推荐
Lee川12 小时前
优雅进化的JavaScript:从ES6+新特性看现代前端开发范式
javascript·面试
Lee川15 小时前
从异步迷雾到优雅流程:JavaScript异步编程与内存管理的现代化之旅
javascript·面试
晴殇i17 小时前
揭秘JavaScript中那些“不冒泡”的DOM事件
前端·javascript·面试
绝无仅有18 小时前
Redis过期删除与内存淘汰策略详解
后端·面试·架构
绝无仅有18 小时前
Redis大Key问题排查与解决方案全解析
后端·面试·架构
AAA梅狸猫19 小时前
Looper.loop() 循环机制
面试
AAA梅狸猫19 小时前
Handler基本概念
面试
Wect19 小时前
浏览器缓存机制
前端·面试·浏览器
掘金安东尼20 小时前
Fun with TypeScript Generics:玩转 TS 泛型
前端·javascript·面试
掘金安东尼20 小时前
Next.js 企业级落地
前端·javascript·面试