leetcode--Populating Next Right Pointers in Each Node II

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    7
After 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
    }
}
个人觉得第一行的注释非常简洁明了,说明了精髓,程序写得很简洁,我是看到注释之后写的代码,没看原来的代码,所以稍微有一些变量不一样,但是可能我写的命名会比较好。
原文地址:https://www.cnblogs.com/obama/p/3252880.html