关于递归和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走一步,
}
}
};
总结:
在做这类题目的时候需多注意递归的边界条件,以及必须弄明白递归函数的代码执行次序(详情见上一篇博文)