1.题目描述

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

相关文章:

  • 2022-01-06
  • 2022-12-23
  • 2022-12-23
  • 2021-04-01
  • 2021-04-13
  • 2021-11-07
猜你喜欢
  • 2021-12-24
  • 2021-11-23
  • 2021-09-18
  • 2021-07-26
  • 2022-01-29
相关资源
相似解决方案