【算法--链表】117.填充每个节点的下一个右侧节点指针Ⅱ--通俗讲解
通俗算法讲解推荐阅读:
【算法–链表】83.删除排序链表中的重复元素–通俗讲解
【算法–链表】删除排序链表中的重复元素 II–通俗讲解
【算法–链表】86.分割链表–通俗讲解
【算法】92.翻转链表Ⅱ–通俗讲解
【算法–链表】109.有序链表转换二叉搜索树–通俗讲解
【算法–链表】114.二叉树展开为链表–通俗讲解
【算法–链表】116.填充每个节点的下一个右侧节点指针–通俗讲解
通俗易懂讲解“填充每个节点的下一个右侧节点指针Ⅱ”算法题目
一、题目是啥?一句话说清
给定一个二叉树,填充每个节点的next指针,使其指向同一层的下一个右侧节点;如果找不到下一个节点,则next指针为NULL。初始时所有next指针都是NULL。
示例:
- 输入:二叉树(可能不完整,即不是满二叉树)
- 输出:二叉树 with next pointers filled
二、解题核心
使用层次遍历,但不需要使用队列,而是利用已建立的next指针来遍历下一层。我们使用一个虚拟节点(dummy)来帮助构建下一层的链表,然后用当前层的next指针来访问所有节点,同时连接下一层的节点。
这就像在每一层中,我们有一个链表,我们遍历这个链表来处理每个节点,同时将它们的子节点连接到下一层的链表中,从而节省空间。
三、关键在哪里?(3个核心点)
想理解并解决这道题,必须抓住以下三个关键点:
1. 利用当前层的next指针遍历
- 是什么:从根节点开始,当前层已经通过next指针连接起来,所以我们可以像遍历链表一样遍历当前层。
- 为什么重要:这样可以避免使用队列,节省空间。我们不需要存储所有节点,只需要几个指针。
2. 构建下一层的链表
- 是什么:在处理当前层的每个节点时,我们将它的左子节点和右子节点依次添加到下一层的链表中。使用一个tail指针来维护下一层链表的尾部。
- 为什么重要:这样我们可以按顺序连接下一层的节点,为下一层的遍历做准备,并保持节点的顺序。
3. 虚拟节点(Dummy Node)的运用
- 是什么:为下一层创建一个虚拟头节点,这样我们可以轻松地访问下一层的头节点。
- 为什么重要:虚拟节点简化了链表操作,避免了处理空链表的情况。当下一层没有节点时,虚拟节点的next为NULL,我们可以停止循环。
四、看图理解流程(通俗理解版本)
假设我们有这样一个二叉树:
1/ \2 3/ \ \4 5 7
我们需要填充next指针。
- 初始化:从根节点开始,当前
current
指向节点1。 - 第一层处理:
- 创建虚拟节点
dummy
,tail
指向dummy
。 - 遍历当前层(只有节点1):
- 节点1有左子节点2:将
tail.next
指向节点2,tail
移动到节点2。 - 节点1有右子节点3:将
tail.next
指向节点3,tail
移动到节点3。
- 节点1有左子节点2:将
- 当前层遍历完后,
current
设置为dummy.next
(即节点2)。
- 创建虚拟节点
- 第二层处理:
- 当前
current
指向节点2(第二层的头)。 - 创建新的虚拟节点
dummy
,tail
指向dummy
。 - 遍历当前层(节点2和节点3通过next连接):
- 节点2有左子节点4:
tail.next
指向节点4,tail
移动到节点4。 - 节点2有右子节点5:
tail.next
指向节点5,tail
移动到节点5。 - 节点3有右子节点7:
tail.next
指向节点7,tail
移动到节点7。
- 节点2有左子节点4:
- 当前层遍历完后,
current
设置为dummy.next
(即节点4)。
- 当前
- 第三层处理:
- 当前
current
指向节点4(第三层的头)。 - 创建虚拟节点
dummy
,tail
指向dummy
。 - 遍历当前层(节点4、5、7通过next连接):
- 节点4没有子节点,跳过。
- 节点5没有子节点,跳过。
- 节点7没有子节点,跳过。
- 下一层为空,循环结束。
- 当前
最终next指针:
- 节点1的next为NULL。
- 节点2的next指向节点3。
- 节点3的next为NULL。
- 节点4的next指向节点5。
- 节点5的next指向节点7。
- 节点7的next为NULL。
五、C++ 代码实现(附详细注释)
#include <iostream>
using namespace std;// 二叉树节点定义
class Node {
public: