114. Flatten Binary Tree to Linked List

关于递归和dfs的一道题目:

描述:
Given a binary tree, flatten it to a linked list in-place.

For example,
Given

     1
    / \
   2   5
  / \   \
 3   4   6

The flattened tree should look like:

   1
  /  \
null  2
     / \
   null 3
    .   \
    .    4
    .     \
    .      5
    .       \
             6

上面第二张图中的null是我自己加上去的,方便理解。
这道题的意思是:
将一棵树转换成一条只有右边节点的单链表,左节点全部为null,flatten是平的意思,就是平的树。

1.递归

遇见树首先想到的应该是递归,这道题目我们可以分析如下:

1.首先判断root节点,只有根元素就直接返回,如果存在左节点或者右节点,我们应该执行的操作是:

if root->left:
    TreeNode* temp = root->left //先保存这个节点,因为之后要置空
    root->left = null
    root->right = temp
    接下来就是递归递归的事情了,因为我们还需要继续判断下面当前的左节点是否还有下一个左节点,右节点的情况简单一点,这里暂时不提

递归这里有点需要注意的,举个例子吧:

    1
   / \
  2   5 
 /     \
 ...   ....下面的暂时不管
在经过上一部之后变成了:
    1
  /   \ 
null   2      ----5保存
      /  \
    ...  ...

到这里也许会问,右节点呢?5呢?怎么全没了?
我们是先将左子树的所有节点全部转换成一个单链表,最后让这条链表的最后一个节点(这个最后一个节点需要不断向下层递归,得到之后再返回,再将其指向5) 指向5即可,完成了拼接。对右边子树(中存在的左子树)进行同样的递归即可,最后就会得到答案。
接下来:

   1                                 1 
  /   \                            /   \
null   2      ----5保存           null  2        ---5保存
      /  \                            /  \  
     3    4                         null  3     ---4保存          这里的4即是最后一个节点,指向5

右节点的情况就比较简单一些,将同层的左子树返回的链表最后一个元素指向它,如这里的右节点5就是左节点2返回的那一条链表2->3->4,让4指向5,就成了2->3->4->5

class Solution {
public:
    void flatten(TreeNode* root) {
        root = Tree_faltten(root);
    }
    TreeNode* Tree_flatten(TreeNode* root){
        if(root == NULL){return NULL;}
        if(root){
            if(root->left || root->right){
                TreeNode* left_temp = root->left;
                TreeNode* right_temp = root->right;
                root->left = NULL;
                if(left_temp){
                    root->right = left_temp;
                    TreeNode* temp = Tree_flatten(left_temp);
                    //每次递归截断在这里,之后返回再继续执行下面的代码,判断这一层是否有右节点,有就指向它,没有直接返回
                    temp->left = NULL;

                    if(right_temp == NULL){
                        return temp;
                    }
                    else{
                        temp->right = right_temp;
                    }
                }
                return Tree_flatten(right_temp);      //对右子树进行递归处理,左子树此时已经链接完成
            } 
        }
        return root;      //在root没有左右子节点时返回root
    }
};

个人觉得看python的代码更容易看懂一些:

class Solution(object):
    def flatten(self, root):
        if not root:
            return None
        root = self.dfs(root)    

    def Tree_faltten(self,root):
        if not (root.left or root.right):
            return root

        if root.left or root.right:
            left = root.left
            right = root.right

            root.left = None
            if left:
                root.right = left
                temp = self.Tree_faltten(left)
                temp.left = None
                if right:
                    temp.right = right
                else:
                    return temp
            return self.Tree_faltten(root.right)

2.dfs

由于python的返回形式比较特殊,这个没写c++版,但大致的过程和上面的差不多

class Solution(object):
    def flatten(self, root):
        if not root:
            return None
        head,tail = self.dfs(root)

    def dfs(self,root):
        if not (root.left or root.right):
            return root,root
        head,tail = root,root
        left_head,left_tail = None,None
        right_head,right_tail = None,None

        if root.left:
            left_head,left_tail = self.dfs(root.left)
        if root.right:
            right_head,right_tail = self.dfs(root.right)
        if left_head:     #如果有左边的指针,将左边的指针变成None,并且链接到右边去
            root.right = left_head
            left_tail.right = right_head   #左节点的下一个是右节点
            tail = right_tail if right_tail else left_tail    #如果有右节点,右节点为tail
        else:
            tail = right_tail
        head.left = None
        return head,tail

3.找规则,拼接链表

class Solution {
public:
    void flatten(TreeNode* root) {
        if(root == NULL){return;}
        while(root){
            if(root->left){
                TreeNode* pre = root->left;  //当前子树根的左节点
                while(pre->right){
                    pre = pre->right;
                }
                pre->right = root->right;  //左节点链接到右节点上
                root->right = root->left;  //当前子树根的右节点指向之前的左节点,相当于全部都成了右节点
                root->left = NULL;
            }
            root = root->right;   //每一次往新的right走一步,       
        }
    } 
};

总结:

在做这类题目的时候需多注意递归的边界条件,以及必须弄明白递归函数的代码执行次序详情见上一篇博文

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值