【算法--链表】116.填充每个节点的下一个右侧节点指针--通俗讲解

71 阅读5分钟

算法通俗讲解推荐阅读
【算法--链表】83.删除排序链表中的重复元素--通俗讲解
【算法--链表】删除排序链表中的重复元素 II--通俗讲解
【算法--链表】86.分割链表--通俗讲解
【算法】92.翻转链表Ⅱ--通俗讲解
【算法--链表】109.有序链表转换二叉搜索树--通俗讲解
【算法--链表】114.二叉树展开为链表--通俗讲解


通俗易懂讲解“填充每个节点的下一个右侧节点指针”算法题目

一、题目是啥?一句话说清

给你一个完美二叉树,填充每个节点的next指针,使其指向同一层的下一个右侧节点;如果没有下一个节点,则设置为NULL。初始时所有next指针为NULL。

示例:

  • 输入:root = [1,2,3,4,5,6,7](完美二叉树)
  • 输出:[1,#,2,3,#,4,5,6,7,#](其中'#'表示NULL,即节点1的next为NULL,节点2的next指向3,节点4的next指向5,等等)

二、解题核心

利用当前层已经建立的next指针来遍历和连接下一层的节点。 这就像组织一个会议,先安排第一排的人手拉手(通过next指针),然后第一排的人帮助第二排的人手拉手,依次类推,直到所有排都连接起来。

三、关键在哪里?(3个核心点)

想理解并解决这道题,必须抓住以下三个关键点:

1. 利用已建立的next指针

  • 是什么:当前层的next指针已经连接好,我们可以用它来遍历当前层,并连接下一层的节点。
  • 为什么重要:这样可以避免使用队列等额外数据结构,实现常数空间复杂度。

2. 分层处理

  • 是什么:从根节点开始,一层一层地处理,直到叶子层。
  • 为什么重要:每层处理完成后,当前层的next指针就完整了,可以用于连接下一层。

3. 连接不同父节点的子节点

  • 是什么:对于当前节点的右子节点,需要将其next指针指向当前节点next节点的左子节点(如果存在)。
  • 为什么重要:这是连接不同父节点的子节点的关键,确保同一层所有节点都能通过next指针连接。

四、看图理解流程(通俗理解版本)

让我们用完美二叉树 [1,2,3,4,5,6,7] 的例子来可视化过程:

  1. 初始化

    • 根节点1的next为NULL。
    • 设置leftmost指针指向根节点1。
  2. 处理第一层(根层)

    • 当前层只有节点1,没有next节点,所以不需要连接同一层。
    • 但需要连接下一层:节点1有左子节点2和右子节点3。
    • 将节点2的next指向节点3。
    • 由于节点1没有next节点,节点3的next保持NULL。
    • 现在第一层处理完成,第二层的next部分连接:2 → 3
  3. 处理第二层

    • leftmost指针移动到节点2(第二层最左节点)。
    • 使用current指针遍历第二层:从节点2开始。
    • 节点2有左子节点4和右子节点5:
      • 将节点4的next指向节点5。
      • 节点2有next节点(节点3),所以将节点5的next指向节点3的左子节点6。
    • 移动到节点3:
      • 节点3有左子节点6和右子节点7:
        • 将节点6的next指向节点7。
      • 节点3没有next节点,所以节点7的next保持NULL。
    • 现在第二层处理完成,第三层的next连接完成:4 → 5 → 6 → 7
  4. 处理第三层

    • leftmost指针移动到节点4。
    • 第三层是叶子层,没有子节点,所以不需要连接下一层。
    • 遍历第三层,确认next指针已正确设置。
  5. 结束:所有层的next指针都已填充。

五、C++ 代码实现(附详细注释)

#include <iostream>
using namespace std;

// 二叉树节点定义
class Node {
public:
    int val;
    Node* left;
    Node* right;
    Node* next;

    Node() : val(0), left(nullptr), right(nullptr), next(nullptr) {}
    Node(int _val) : val(_val), left(nullptr), right(nullptr), next(nullptr) {}
    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 nullptr;
        }
        
        // 从根节点开始,leftmost用于跟踪每一层的最左节点
        Node* leftmost = root;
        
        // 当还有下一层时继续循环
        while (leftmost->left != nullptr) {
            // 当前层的当前节点
            Node* current = leftmost;
            
            // 遍历当前层,连接下一层的节点
            while (current != nullptr) {
                // 连接相同父节点的子节点
                current->left->next = current->right;
                
                // 连接不同父节点的子节点
                if (current->next != nullptr) {
                    current->right->next = current->next->left;
                }
                
                // 移动到当前层的下一个节点
                current = current->next;
            }
            
            // 移动到下一层的最左节点
            leftmost = leftmost->left;
        }
        
        return root;
    }
};

// 辅助函数:打印树的层次结构(通过next指针)
void printTreeByLevel(Node* root) {
    Node* levelStart = root;
    while (levelStart != nullptr) {
        Node* current = levelStart;
        while (current != nullptr) {
            cout << current->val << " ";
            current = current->next;
        }
        cout << "# "; // 表示层结束
        levelStart = levelStart->left; // 移动到下一层
    }
    cout << endl;
}

// 测试代码
int main() {
    // 构建示例完美二叉树:[1,2,3,4,5,6,7]
    Node* root = new Node(1);
    root->left = new Node(2);
    root->right = new Node(3);
    root->left->left = new Node(4);
    root->left->right = new Node(5);
    root->right->left = new Node(6);
    root->right->right = new Node(7);
    
    Solution solution;
    solution.connect(root);
    
    printTreeByLevel(root); // 输出:1 # 2 3 # 4 5 6 7 #
    
    // 释放内存(实际面试中可能不需要完整释放)
    return 0;
}

六、注意事项

  • 完美二叉树假设:题目明确是完美二叉树,所以我们可以安全地访问left和right子节点,无需空值检查。如果不是完美二叉树,这种方法需要调整。
  • 空间复杂度:这种方法使用常数额外空间,只用了几个指针变量,优于使用队列的层次遍历(O(n)空间)。
  • 指针操作:在连接节点时,确保不会访问空指针。由于是完美二叉树,当前层有子节点时,下一层一定存在。
  • 遍历顺序:从左到右遍历当前层,利用next指针移动,效率很高。
  • 边界情况:处理空树时直接返回nullptr。