116. Populating Next Right Pointers in Each Node

本文介绍了一种算法问题的两种解决方案:一种是利用队列进行层级遍历的方法;另一种是采用递归的方式,通过左右子节点间的连接来完成。这两种方法均可使二叉树的每个节点指向其右侧相邻节点。

Given a binary tree

struct TreeLinkNode {
  TreeLinkNode *left;
  TreeLinkNode *right;
  TreeLinkNode *next;
}

Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL.

Initially, all next pointers are set to NULL.

Note:

You may only use constant extra space.
You may assume that it is a perfect binary tree (ie, all leaves are at the same level, and every parent has two children).
For example,
Given the following perfect binary tree,

    1
   /  \
  2    3
 / \  / \
4  5  6  7

After calling your function, the tree should look like:

    1 -> NULL
   /  \
  2 -> 3 -> NULL
 / \  / \
4->5->6->7 -> NULL

解法一,使用队列,时间复杂度O(N):

/**
 * Definition for binary tree with next pointer.
 * struct TreeLinkNode {
 *  int val;
 *  TreeLinkNode *left, *right, *next;
 *  TreeLinkNode(int x) : val(x), left(NULL), right(NULL), next(NULL) {}
 * };
 */
class Solution {
public:
    void connect(TreeLinkNode *root) {
        if(root == NULL)
            return ;

        std::queue<TreeLinkNode*> que;
        que.push(root);

        while(!que.empty()){
            const int size = que.size();
            for(int i=0; i<size; ++i){
                TreeLinkNode* tmp = que.front();
                que.pop();

                if(!que.empty() && i != size-1)
                    tmp->next = que.front();
                //default tmp1->next = NULL;

                if(tmp->left != NULL)
                    que.push(tmp->left);
                if(tmp->right != NULL)
                    que.push(tmp->right);
            }
        }
    }
};

解法二,递归:

class Solution {
public:
    void connect(TreeLinkNode *root) {
        if(root == NULL)
            return ;
        if(root->left != NULL){
            root->left->next = root->right; //perfect binary tree
            if(root->next != NULL)
                root->right->next = root->next->left;
        }
        connect(root->left);
        connect(root->right);
    }
};

这道题一开始我使用递归,我一直想借助后续遍历的思路。事实证明,不要死搬硬套!要针对实际情况递归。

<think>好的,用户的问题是关于链表节点标签13的next指针在原始链表中被修改的解决方案。首先,我需要理解用户提到的具体问题。根据引用[4]中的描述,处理链表复制时,原链表尾节点的next指针需要单独设置为null,否则会报错。用户现在遇到类似的问题,但节点标签是13,可能是在处理链表复制或其他操作时,原链表的节点13的next指针被意外修改了。 接下来,我需要参考用户提供的引用内容。引用[4]中的解决方案分为三步:复制节点、设置random指针、拆分链表。在拆分过程中,原链表的节点next指针被修改,但最终需要恢复原链表的next指针,或者确保新链表正确分离。例如,在拆分时,原链表的每个节点的next应该指向原来的下一个节点,而新链表的节点则指向复制的节点。如果在这个过程中没有正确处理,可能导致原链表的节点next指针错误,比如标签13的节点的next被修改了。 可能的错误原因有几个方面:首先,在拆分链表时,可能没有正确恢复原链表的next指针,尤其是在处理到最后一个节点时,如引用[4]提到的需要将pre.next设为null。其次,可能在循环拆分过程中,某个步骤处理不当,导致原链表节点的next指向了新链表的节点,而非原链表的下一个节点。例如,当处理到标签13的节点时,如果在拆分时没有正确跳过复制的节点,就会导致原链表的结构被破坏。 解决方案方面,引用[4]中的代码在拆分后,将原链表的尾节点的next设置为null,这可能是为了确保原链表的结构正确。但用户的问题可能出现在中间节点,比如标签13的节点,因此需要检查拆分过程中的循环逻辑是否正确处理了所有节点,尤其是当原链表的节点后面跟着复制节点时,必须正确地将原节点的next指向原链表的下一个节点,而不是复制节点。例如,在拆分循环中,原链表节点pre的next应该指向pre.next.next(即原链表的下一个节点),而新链表节点cur的next指向cur.next.next。如果在处理到标签13的节点时,循环没有正确执行这一步,就会导致next指针错误。 另外,可能还需要考虑边界情况,比如链表只有一个节点,或者要处理的节点是头节点或尾节点。例如,如果标签13的节点是原链表的尾节点,那么在拆分时可能需要像引用[4]那样单独处理,将pre.next设为null。但如果是中间节点,则需要在循环中正确更新pre和cur的next指针。 总结可能的解决步骤:1. 确保在拆分链表时,每一步都正确恢复原链表节点的next指针。2. 检查循环逻辑,尤其是处理到标签13的节点时,是否正确地跳过了复制的节点。3. 处理尾节点时,单独将原链表尾节点的next设为null,如引用[4]中的做法。4. 验证原链表在操作后的完整性,确保每个节点的next指针没有被错误修改。 此外,用户可能需要具体的代码示例来展示如何正确拆分链表,同时保持原链表的结构。根据引用[4]的代码,拆分部分的循环应该正确地将原链表和新链表分离,通过pre和cur指针分别处理原链表和新链表的next指针。如果在处理到标签13的节点时出现错误,可能需要检查循环条件是否正确,例如cur.next是否为null,或者在更新指针时是否遗漏了某个步骤。 最后,生成相关问题需要考虑用户可能进一步的问题,比如如何验证链表结构、其他常见错误或优化方法等。</think>### 链表节点标签13的next指针被修改的解决方案 当链表操作中遇到“Next pointer of node with label 13 modified in original list”报错时,通常是因为**在复制或修改链表时未正确恢复原链表的节点指针**,导致原链表结构被破坏。以下是具体原因和解决方案: --- #### 1. 问题原因分析 - **指针未恢复**:在带有复制的操作中(如复制带随机指针的链表),拆分新旧链表时,原链表的`next`指针可能被错误地指向新链表的节点。例如,标签13的原节点`next`可能未指向原链表的下一个节点,而是指向了复制节点。 - **尾节点未处理**:若标签13是原链表的尾节点,拆分后未显式设置其`next`为`null`,可能导致原链表尾指针残留引用[^4]。 --- #### 2. 解决方案 ##### 方法1:严格拆分新旧链表(参考引用[4]) 在复制链表后,通过三次遍历确保原链表和新链表完全分离: 1. **复制节点**:将新节点插入原节点之后,形成`原节点 -> 新节点 -> 原下一节点`的结构。 2. **设置随机指针**:根据原节点的`random`指针,更新新节点的`random`。 3. **拆分链表**:遍历链表,恢复原链表指针并提取新链表。 ```java // 以Java示例(基于引用[4]) public Node copyRandomList(Node head) { if (head == null) return null; Node cur = head; // 1. 复制节点 while (cur != null) { Node copy = new Node(cur.val); copy.next = cur.next; cur.next = copy; cur = copy.next; } // 2. 设置random指针 cur = head; while (cur != null) { if (cur.random != null) { cur.next.random = cur.random.next; } cur = cur.next.next; } // 3. 拆分链表 cur = head.next; Node pre = head, res = head.next; while (cur.next != null) { pre.next = pre.next.next; // 恢复原链表指针 cur.next = cur.next.next; // 构建新链表指针 pre = pre.next; cur = cur.next; } pre.next = null; // 单独处理原链表尾节点 return res; } ``` **关键点**: - **拆分时恢复原链表**:在第三步循环中,`pre`指针始终指向原链表的节点,通过`pre.next = pre.next.next`跳过复制节点,恢复原链表的`next`指针[^4]。 - **尾节点处理**:显式设置原链表尾节点的`next`为`null`,避免残留引用。 --- ##### 方法2:哈希表辅助(空间换时间) 若允许使用额外空间,可通过哈希表记录原节点到复制节点的映射,避免修改原链表: ```java public Node copyRandomList(Node head) { Map<Node, Node> map = new HashMap<>(); Node cur = head; while (cur != null) { map.put(cur, new Node(cur.val)); cur = cur.next; } cur = head; while (cur != null) { Node copy = map.get(cur); copy.next = map.get(cur.next); copy.random = map.get(cur.random); cur = cur.next; } return map.get(head); } ``` **优点**:无需修改原链表,彻底避免指针错误。 --- #### 3. 验证原链表完整性 操作后,遍历原链表并检查每个节点的`next`指针是否指向正确的原链表节点。例如,标签13的节点应满足: ```java // 假设原链表为1 -> 2 -> ... -> 13 -> 14 -> null Node node13 = head; for (int i = 1; i < 13; i++) node13 = node13.next; assert node13.next.val == 14; // 确保未被修改为复制节点 ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值