二叉树(思路)
二叉树做题思维模型!!
二叉树解题的思维模式分两类:
1、是否可以通过遍历一遍二叉树得到答案?如果可以,用一个 traverse
函数配合外部变量来实现,这叫「遍历」的思维模式。
2、是否可以定义一个递归函数,通过子问题(子树)的答案推导出原问题的答案?如果可以,写出这个递归函数的定义,并充分利用这个函数的返回值,这叫「分解问题」的思维模式。
无论使用哪种思维模式,你都需要思考:
如果单独抽出一个二叉树节点,它需要做什么事情?需要在什么时候(前/中/后序位置)做?其他的节点不用你操心,递归函数会帮你在所有节点上执行相同的操作。
226. 翻转二叉树
给你一棵二叉树的根节点 root
,翻转这棵二叉树,并返回其根节点。
思路
是否可以通过遍历实现?
可以。写一个 traverse
函数遍历每个节点,让每个节点的左右子节点颠倒过来就行了。
单独抽出一个节点,需要让它做什么?让它把自己的左右子节点交换一下。
需要在什么时候做?好像前中后序位置都可以。
遍历实现:
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (root == NULL) return root;
swap(root->left, root->right); // 中
invertTree(root->left); // 左
invertTree(root->right); // 右
return root;
}
};
递归分解实现
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(!root) return root;
TreeNode* left = invertTree(root->left);
TreeNode* right = invertTree(root->right);
root->left = right;
root->right = left;
return root;
}
};
116. 填充每个节点的下一个右侧节点指针
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。
思路
你可以把二叉树的相邻节点抽象成一个「三叉树节点」,这样二叉树就变成了一棵「三叉树」,然后你去遍历这棵三叉树,把每个「三叉树节点」中的两个节点连接就行
这个思路确实难想,但是画图的时候,连接相邻结点其实就两种情况
- 连接父节点相同的两个子节点
- 连接父节点不同的两个节点
然后递归实现就行
代码实现
class Solution {
public:
Node* connect(Node* root) {
if(!root) return root;
traverse(root->left,root->right);
return root;
}
void traverse(Node* l,Node* r){
if(!l || !r) return;
//前序遍历,将传入的两个节点连接起来
l->next = r;
//连接父节点相同的两个节点
traverse(l->left,l->right);
traverse(r->left,r->right);
//连接相邻但是不同父节点的两个子节点
traverse(l->right,r->left);
}
};
114. 二叉树展开为链表
给你二叉树的根结点 root
,请你将它展开为一个单链表:
- 展开后的单链表应该同样使用
TreeNode
,其中right
子指针指向链表中下一个结点,而左子指针始终为null
。 - 展开后的单链表应该与二叉树 先序遍历 顺序相同。
思路:
很简单,以下流程:
1、将 root
的左子树和右子树拉平。
2、将 root
的右子树接到左子树下方,然后将整个左子树作为右子树。
class Solution {
public:
void flatten(TreeNode* root) {
if(!root) return;
//先递归拉平左右子树
flatten(root->left);
flatten(root->right);
//后序遍历位置 对当前结点的处理
//1、左右子树已经被拉成一条链表
TreeNode* l = root->left;
TreeNode* r = root->right;
//2、处理当前结点的作用子树,将左子树作为右子树,右子树放在左子树最右
root->left = NULL;
root->right = l;
//3、将原来的右子树放在左子树的最右端
TreeNode* p = root;
while(p->right){
p = p->right;
}
p->right = r;
}
};