[LeetCode 116 117] - 填充每一个节点的指向右边邻居的指针I & II (Populating Next Right Pointers in Each Node I & II)
问题
给出如下结构的二叉树:
struct TreeLinkNode {
TreeLinkNode *left;
TreeLinkNode *right;
TreeLinkNode *next;
}
填充每一个next指针使其指向自己的右边邻居节点。如果没有右边的邻居节点,next指针须设成NULL。
在开始时,所有的next指针被初始化成NULL。
注意:
- 你只能使用常数级别的额外空间
- 你可以假设该树为完全二叉树(即所有叶子节点都在同一层,而且每个父节点都有两个子节点)。
例如,给出如下完全二叉树:
1
/ \
2 3
/ \ / \
4 5 6 7
在调用你的函数后,树看起来将是这样:
1 -> NULL
/ \
2 -> 3 -> NULL
/ \ / \
4->5->6->7 -> NULL
初始思路
要得到每个节点的右边邻居,显然应该对二叉树进行层次遍历。使用队列+循环是进行二叉树层次遍历的标准方法。但是这里我们有一点特别的需求-需要知道每一层的结束,因为每一层最右边节点的next指针需要设为null。回想我们在 [LeetCode 126] - 单词梯II(Word Ladder II) 中最后提到的双vector交替循环法,在这里特别适用:遍历当前vector得到的子节点都放到下一个vector中,当遍历完毕时,下一个vector中恰好就是下一层的所有节点。我们只需将其中每个元素的next指针指向自己的下一个元素即可,当然,最后一个元素要跳过。然后清空当前vector,交换当前vector和下一个vector(通过对下标取反)并重复此步骤直到当前vector为空就完成了任务。最后完成的代码如下:
class Solution {
public:
void connect(TreeLinkNode *root)
{
if(!root)
{
return;
}
candidates_[].clear();
candidates_[].clear();
int flag = ;
candidates_[flag].push_back(root);
while(!candidates_[flag].empty())
{
for(auto iter = candidates_[flag].begin(); iter != candidates_[flag].end(); ++iter)
{
TreeLinkNode* node = *iter;
if(node->left)
{
candidates_[!flag].push_back(node->left);
}
if(node->right)
{
candidates_[!flag].push_back(node->right);
}
}
if(candidates_[!flag].empty())
{
break;
}
for(auto iter = candidates_[!flag].begin(); iter != candidates_[!flag].end() - ; ++iter)
{
(*iter)->next = *(iter + );
}
candidates_[flag].clear();
flag = !flag;
}
}
private:
std::vector<TreeLinkNode*> candidates_[];
};
connect
上面的代码能通过Judge Small和Judge Large的测试,但是仔细审题后可以发现,其实我们的方案并不符合题目的要求-你只能使用常数级别的额外空间。使用队列对二叉树进行层次遍历,队列需要的最大空间是和二叉树的大小有关的,对题目中的完全二叉树来说,它等于叶子节点的个数。即对层数为n(n>0)的二叉树,需要的空间为2^(n-1)级别。
改进方案
要符合题目的要求,看来是不能使用循环遍历队列的方式来层次遍历二叉树了。遍历二叉树,除了循环无非就是递归。对层次遍历来说,递归的方法并不是那么常用,那么,让我们来看看用递归怎么层次遍历二叉树。
想象一下我们要访问一个二叉树的第n层,我们会从顶点出发,一层层往下直到第n层。如果把一次往下走的这个动作看作一次函数调用,那么我们就可以得到一串递归调用。这个递归的结束条件是什么?从顶点往下一层后,还需要走n-1层能达到我们的目标,再下一层,需要n-2层。如此往复,当n=1时就走到了我们期望的层数。由此n=1就是递归结束的条件。用一个三层的完全二叉树来模拟递归访问第三层,情况如下:
Visit(node1, 3)
1
Visit(node1->left, 2) / \ Visit(node1->right, 2)
2 3
Visit(node2->left, 1) / \ Visit(node2->right, 1) Visit(node3->left, 1) / \ Visit(node3->right, 1)
4 5 6 7
由此,我们得到伪代码如下:
访问二叉树(节点,层数)
如果层数=1,访问当前节点
否则
访问二叉树(节点的左子树,层数-1)
访问二叉树(节点的右子树,层数-1)
现在我们得到了递归访问二叉树第n层的方法,但是离访问所有层还是有一段距离。要访问所有层,我们需要让n从1开始递增,循环调用访问二叉树第n层的递归函数。什么时候不需要再递增n了呢?不需要再递增层数意味着我们的n已经走到了二叉树的底层,根据题目中完全二叉树的条件,底层也就是叶子节点所在的层。而叶子节点的特性-左右子节点皆为null就为我们提供了检查的标准。由此我们就需要在上面两个对左右子树的递归调用中各返回一个指针,当返回的两个指针皆为null时,说明我们到达底层了。符合递归结束条件时,自然是返回当前节点,那递归过程中返回哪个指针?其实这里在左右指针中随便选一个就行了,因为我们只需要利用返回值是否为空指针这个信息,而对完全二叉树来说左右子节点的这个性质必然是一致的。
最后,不要忘了我们的目的是要为每个节点设置next指针,所以我们需要一个成员变量来保存左边邻居。每当访问到一个节点,我们将左边邻居的next指针指向它,然后将它的地址拷贝到保存左邻居的指针中。当然,不要忘了要做一些特殊判断来处理每层第一个节点的情况。
class Solution {
public:
void connect(TreeLinkNode *root)
{
if(!root)
{
return;
}
hasToTheEnd_ = false;
int level = ;
while(!hasToTheEnd_)
{
nextLeft_ = nullptr;
VisitLevel(root, level);
++level;
}
}
private:
TreeLinkNode* VisitLevel(TreeLinkNode* node, int level)
{
if(level == )
{
if(nextLeft_ != nullptr)
{
nextLeft_ ->next = node;
}
nextLeft_ = node;
return node;
}
TreeLinkNode* left = VisitLevel(node->left, level - );
TreeLinkNode* right = VisitLevel(node->right, level - );
if(left == nullptr && right == nullptr)
{
hasToTheEnd_ = true;
}
//完全二叉树,左右子树的非空性必然一致,随便返回一个即可
return left;
}
TreeLinkNode* nextLeft_;
bool hasToTheEnd_;
};
connect_v2
现在的代码就完全符合题目要求并且能通过大小数据集测试了。
后继问题
如果给出的树为任意二叉树,前面的解决方案还能工作吗?
后继思路
如果去掉了完全二叉树这个条件,那么我们用来判断是否到达二叉树底层的方法就不再生效了,如下面的二叉树:
1
/ \
2 3
/ \
4 5
可以看到编号为3的叶子节点并不是最底层。
我们怎么知道3号节点并不是最底层?因为和它在同一层的2号节点还存在子节点。那么我们可以更改一下判断最底层的条件:如果当前访问的层有任一节点存在子节点,说明当前层不是最底层。什么时候我们走到了当前访问的层?n = 1递归条件结束的时候。我们可以在这里判断当前节点是否存在子节点,如果有就设置继续循环标志。也就是说,我们把判断当前层是否为最底层的逻辑由“如果符合某条件,则终止循环”变为了“如果符合某条件,则继续循环”。因此相应的我们需要在完成针对一层的循环后将标志复位。做了上面的修改后,可以发现递归函数的返回值不再需要了,因为我们在最后一次递归调用返回前就完成了判断而不再通过返回信息供上层函数判断。得到的最终代码如下,现在处理任意二叉树都没有问题了:
class Solution{
public:
void connect(TreeLinkNode *root)
{
if(!root)
{
return;
}
hasToTheEnd_ = false;
int level = ;
while(!hasToTheEnd_)
{
hasToTheEnd_ = true;
nextLeft_ = nullptr;
VisitLevel(root, level);
++level;
}
}
private:
void VisitLevel(TreeLinkNode* node, int level)
{
if(level == )
{
if(nextLeft_ != nullptr)
{
nextLeft_ ->next = node;
}
nextLeft_ = node;
if(node->left != nullptr || node->right != nullptr)
{
hasToTheEnd_ = false;
}
}
if(node->left)
{
VisitLevel(node->left, level - );
}
if(node->right)
{
VisitLevel(node->right, level - );
}
}
TreeLinkNode* nextLeft_;
bool hasToTheEnd_;
};
connect_117
[LeetCode 116 117] - 填充每一个节点的指向右边邻居的指针I & II (Populating Next Right Pointers in Each Node I & II)的更多相关文章
- leetcode@ [116/117] Populating Next Right Pointers in Each Node I & II (Tree, BFS)
https://leetcode.com/problems/populating-next-right-pointers-in-each-node-ii/ Follow up for problem ...
- LeetCode:Populating Next Right Pointers in Each Node I II
LeetCode:Populating Next Right Pointers in Each Node Given a binary tree struct TreeLinkNode { TreeL ...
- 【leetcode】Populating Next Right Pointers in Each Node I & II(middle)
Given a binary tree struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode *nex ...
- LeetCode 116/117. 填充同一层的兄弟节点(Populating Next Right Pointers in Each Node)
题目描述 给定一个二叉树 struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode *next; } 填充 ...
- [Java]LeetCode117. 填充同一层的兄弟节点 II | Populating Next Right Pointers in Each Node II
Given a binary tree struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode *nex ...
- LeetCode 题解:Populating Next Right Pointers in Each Node I & II 二有难度。考虑不全面。
每次应该把root同层的右侧节点传过来.如果没有,就传NULL. 同时,应该是先右后左. 感觉这次的代码还挺简洁的.. void construct(struct TreeLinkNode *root ...
- Java for LeetCode 117 Populating Next Right Pointers in Each Node II
Follow up for problem "Populating Next Right Pointers in Each Node". What if the given tre ...
- leetcode 199. Binary Tree Right Side View 、leetcode 116. Populating Next Right Pointers in Each Node 、117. Populating Next Right Pointers in Each Node II
leetcode 199. Binary Tree Right Side View 这个题实际上就是把每一行最右侧的树打印出来,所以实际上还是一个层次遍历. 依旧利用之前层次遍历的代码,每次大的循环存 ...
- [LeetCode] 116. Populating Next Right Pointers in Each Node 每个节点的右向指针
You are given a perfect binary tree where all leaves are on the same level, and every parent has two ...
随机推荐
- mount 远程挂载Nfs
服务器:192.168.20.204 客户端:192.168.20.203 1. 在服务器配置/etc/export 添加可以共享的文件夹和允许的客户端地址 /home/dir 192.168.20. ...
- 基于JAVA的webVNC
jxpiInstall安装程序下载: http://sdlc-esd.sun.com/ESD6/JSCDL/jdk/7u60-b19/jxpiinstall.exe?AuthParam=1402208 ...
- SKView类
继承自 UIView:UIResponder:NSObject 符合 NSCoding(UIView)UIAppearance(UIView)UIAppearanceContainer(UIView) ...
- oracle中LAG()和LEAD()等分析统计函数的使用方法(统计月增长率)
LAG()和LEAD()统计函数能够在一次查询中取出同一字段的前N行的数据和后N行的值.这样的操作能够使用对同样表的表连接来实现,只是使用LAG和 LEAD有更高的效率.下面整理的LAG()和LEAD ...
- Android TabActivity之感叹
(一)前言 在以前一篇帖子讲ams的时候,提了一下TabActivity.当时说它比较特殊就没有下文了,今天重发一篇帖子,跟大家探讨一下TabActivity. 做个假定先: 比如我们最外面的Acti ...
- 不相交集python实现
1.不相交集是解决等价关系的一种数据结构,执行合并和查找的速度都很快,M次执行合并和查找的执行时间为(M*logN). 在一个集合中.对于每一对元素(a,b),a,b∈S,对于关系R假设满足以下三个条 ...
- 一个简单的Verilog计数器模型
一个简单的Verilog计数器模型 功能说明: 向上计数 向下计数 预装载值 一.代码 1.counter代码(counter.v) module counter( input clk, input ...
- 读书笔记--用Python写网络爬虫01--网络爬虫简介
Wiki - Web crawler 百度百科 - 网络爬虫 1.1 网络爬虫何时使用 用于快速自动地获取网络信息,避免重复性的手工操作. 1.2 网络爬虫是否合法 网络爬虫目前人处于早期的蛮荒阶段, ...
- 在Blade中结合gperftools检查内存泄露
Blade是我们开发的大规模C++项目构建工具. gperftools是google开发的性能工具,由高效内存分配器,CPU性能分析器,堆分析器,堆检查器等工具组成. 和其他构建工具不同,结合gtes ...
- javascript:void(0)知多少
在做页面时,如果想做一个链接点击后不做任何事情,或者响应点击而完成其他事情,可以设置其属性 href = "#",但是,这样会有一个问题,就是当页面有滚动条时,点击后会返回到页面顶 ...