Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
前言
力扣第117题 填充每个节点的下一个右侧节点指针 II 如下所示:
给定一个二叉树
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
进阶:
- 你只能使用常量级额外空间。
- 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
示例:
输入:root = [1,2,3,4,5,null,7]
输出: [1,#,2,3,#,4,5,7,#]
解释: 给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化输出按层序遍历顺序(由 next 指针连接),'#' 表示每层的末尾。
一、思路
这一题与前面 力扣第116题-填充每个节点的下一个右侧节点指针 不同的是不是完全二叉树。
所以在 递归 中利用同级节点的 next 会出现右侧节点还没有建立好 next 指针,而导致下一层节点的指向出现错误。至于具体怎么做的,不妨继续向下看看把!
先列举以下题目中的重要信息:
- 树不为完全二叉树
next需指向同层级右侧的下一个节点
有一种简单的方法可以实现上述的需求:遍历树的过程中使用栈或者链表保存当前层级的节点,并依次改变他们的 next 指向。当时这样就不能很好的利用上一层已经初始化过的 next 指针了。
在这里我们依然选择使用 递归 来完成这一题,但是在递归的过程中会出现xxx
图解算法
此处以如下的二叉树作为例子,我们以 根左右 前序遍历的方式递归遍历这个树
以如下的规则来初始化 next 指向
- 如当前节点为叶子节点,则返回该节点、
- 如当前节点为空,则返回
null - 如有左右孩子,则将左孩子指向右孩子。再将右孩子指向同层级右侧节点
- 将不为空的左孩子(或右孩子)指向同层级右侧节点
- 从根节点出发,将左孩子
2.next指向3
- 继续处理节点
2,将左孩子0.next指向7,再将右孩子7指向同层级右侧节点
- 继续处理节点
7,7.next指向1
- 向上回溯,继续处理
2的右孩子7。
将 7 左孩子 1.next 指向 0
此时发现当前节点的 7.next 指向的为 9,但是 9 为叶子节点。无法通过当前节点 next 的指向到达同级的节点 6
故无法正确初始化右孩子 0.next 的指向。
通过上面的例子我们发现:如果 根左右 的方式递归遍历树的话,会出现无法到达最右侧节点的情况。故我们按照 根右左 的方式递归遍历树,就能保证同层所有节点均可达。
为什么
根右左递归遍历可以保证同层所有节点均可达呢? 其实很简单,因为最右侧节点next的指向为null,并不需要依靠左侧的节点来初始化next
二、实现
实现代码
实现代码与思路保持一致,使用的递归的方式实现
public Node connect(Node root) {
if (root == null)
return null;
if (root.left == null && root.right == null) // 叶子节点
return root;
if (root.left != null && root.right != null){ // 左右节点都有
root.left.next = root.right;
}
// 完成当前层所有next的指向
if (root.next != null){
// 使用循环找出上一级中的右边的元素
Node preLastNode = null;
Node tempNext = root.next;
while (tempNext != null){
if (tempNext.left != null){
preLastNode = tempNext.left;
break;
}else if (tempNext.right != null){
preLastNode = tempNext.right;
break;
}
tempNext = tempNext.next;
}
// 判断是赋值左孩子还是右孩子
if (root.right == null){
root.left.next = preLastNode;
}else {
root.right.next = preLastNode;
}
}
connect(root.right);
connect(root.left);
return root;
}
测试代码
public static void main(String[] args) {
Node node = new Node(1,
new Node(2, new Node(4), new Node(5), null),
new Node(3, null, new Node(7), null),
null);
Node node1 = new Node(1,
new Node(2, new Node(4, new Node(7), null, null), new Node(5), null),
new Node(3, null, new Node(6, null, new Node(8), null), null),
null);
Node node2 = new Node(0,
new Node(2, new Node(1, new Node(5), new Node(1), null), null, null),
new Node(4, new Node(3, null, new Node(6), null), new Node(-1, null, new Node(8), null), null),
null);
Node node3 = new Node(2,
new Node(1,
new Node(0, new Node(2), null, null),
new Node(7, new Node(1), new Node(0, new Node(7), null, null), null),
null),
new Node(3,
new Node(9, null, null, null),
new Node(1, new Node(8), new Node(8), null),
null),
null);
Node node4 = new Node(0,
new Node(2,
new Node(1, new Node(5), new Node(1), null),
null,
null),
new Node(4,
new Node(3, null, new Node(6), null),
new Node(-1, null, new Node(8), null),
null),
null);
Node ret = new Number117().connect(node4);
System.out.println(ret);
}
结果
三、总结
这一题我写完代码后调试了两个小时才最终完成,大家看我的测试用例就能发现了。可见调试对于写程序的重要性!
感谢看到最后,非常荣幸能够帮助到你~♥
如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~