1.题目描述
Follow up for problem "Populating Next Right Pointers in Each Node".What if the given tree could be any binary tree? Would your previous solution still work?Note:You may only use constant extra space.For example,Given the following binary tree,1/2 3/4 5 7After calling your function, the tree should look like:1 -> NULL
/2 -> 3 -> NULL/4-> 5 -> 7 -> NULL
2.解题思路
上一篇文章中介绍了当给定二叉树为满二叉树的情况怎么将树转换,从那篇文章中可以看出,满二叉树的情况是相当好计算的。这个题目放宽了条件,现在要求我们对任意二叉树进行上述转换,一下子看起来复杂多了,那么此题又当如何解呢?
一个很直接的思路就是仍然用满二叉树的思路来解,既然仍然想用二叉树的机构来解答问题,那么首先需要将普通二叉树转换成满二叉树的情况,对于所示的二叉树:
Given the following binary tree,1/2 3/4 5 7/8 11
在处理之前,我们可以先将其转化为这棵树
Given the following binary tree,1/2 3/ /4 5 s 7/ / / /8 s s 11 s s s s
转换过程有两个需要注意的地方 :
- 任意一个包含有效数据的节点的子节点如果为空,则该子节点用一个特殊的节点代替,图中表示为 s ,在代码中表现为special,为了节省空间,所有的s均拥有同样的地址,由于处理过程是层序遍历,我们只是借用s的当前状态,所以这不会造成问题。
- s节点的两个子节点也分别设置为s
- 在层序遍历过程中,s的left指针指向当前层遍历过程中最后一个非s的节点,一旦遍历到该层的下一个非s节点,则可以让这两个非s节点连接起来。
有了上述描述之后很容易得到下面的代码:
/**
* 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) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
if(root == NULL)return;int maxDepth = getMaxDepth(root);
TreeLinkNode * special = NULL;
int maxCount = pow(2,maxDepth)-1;
TreeLinkNode *cur = NULL;TreeLinkNode *pre = NULL;int count =0;
int depth =1;
queue<TreeLinkNode *> q;q.push(root);while(count<maxCount)
{cur = q.front();if(cur!=special)
{q.push(cur->left==NULL?special:cur->left);q.push(cur->right==NULL?special:cur->right);}else
{q.push(special);q.push(special);}q.pop();count ++;if(count == (pow(2,depth)-1)){
if(cur != special)
{if(pre!=NULL)pre->next = cur;
cur->next = NULL;}depth++;pre=NULL;}else
{if(cur!=special){
if(pre!=NULL){
pre->next = cur;pre= cur;cur->next = q.front();}else
{pre = cur;}}}}}private:
int getMaxDepth(TreeLinkNode *root)
{if(root == NULL)return 0;return max(getMaxDepth(root->left),getMaxDepth(root->right))+1;
}};对于小数据集,该代码一下子就AC了,但是大数据集却始终不行,而且在该过程中发现,special只能用NULL代替,如果new一个内存给special的话就有runtime error,希望以后能弄明白为啥,今天想了一天没想清楚。
如果对上述代码不太明了,可以参考上一篇关于满二叉树转换的代码,可能思路会清晰一些。
那么,为什么大数据集通不过了,分析上面的代码可知,对于一个链表形状的树,大部分时间实在遍历一些实际上并不存在的点,我的本意是模拟层序遍历,那么直接用层序遍历的方式来做就好了,但是由于要处理边界条件并像满二叉树靠拢,我不得已虚构了很多点,实际上,我们可以用动态的观点来看问题,对于第i层,如果我们已经完成了转换,那么第i层实际上已经由next指针域串成了一个链表,这个链表就省去了我们在层序遍历过程中对于队列的需要了,于是,我们只需记录第i层链表的的头指针,就可以依次处理第i+1层,直至处理完为止,基于这样的思路,我们有如下代码:
/**
* 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) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
if(root == NULL)return ;root->next = NULL;TreeLinkNode * curHead = root;while(curHead != NULL)
{TreeLinkNode * p=curHead;TreeLinkNode *cur = NULL;TreeLinkNode *pre = NULL;curHead = NULL;while(p!=NULL)
{if(p->left != NULL)
{cur = p->left;if(pre != NULL) pre->next = cur;
pre = cur;if(curHead == NULL) curHead = p->left;
}if(p->right != NULL)
{cur = p->right;if(pre != NULL) pre->next = cur;
pre = cur;if(curHead == NULL) curHead = p->right;
}p = p->next;}}}};上面的代码脱胎于:http://discuss.leetcode.com/questions/282/populating-next-right-pointers-in-each-node-ii,原始代码如下:
// the link of level(i) is the queue of level(i+1)
void connect(TreeLinkNode * n) {
while (n) {
TreeLinkNode * next = NULL; // the first node of next level
TreeLinkNode * prev = NULL; // previous node on the same level
for (; n; n=n->next) {
if (!next) next = n->left?n->left:n->right;
if (n->left) {
if (prev) prev->next = n->left;
prev = n->left;}if (n->right) {
if (prev) prev->next = n->right;
prev = n->right;}}n = next; // turn to next level
}}
个人觉得第一行的注释非常简洁明了,说明了精髓,程序写得很简洁,我是看到注释之后写的代码,没看原来的代码,所以稍微有一些变量不一样,但是可能我写的命名会比较好。