labuladong学习算法和刷题的框架思维
124. 二叉树中的最大路径和_后序遍历
- 采用后序遍历:先访问左子树,再访问右子树,最后根据左右子树的结果和当前节点更新当前节点对应的最大值
maxVal
记录的是全局最大路径和(可以不返回,因为它始终在递归过程中更新即可,递归结束时,这个值也就出来了)- 定义递归函数的意义:
- 定义dfs函数:返回root的左(或右)子树能向root节点“提供”的最大路径和。即,一条从父节点延伸下来的路径,能在当前子树中获得的最大收益。也就是:
dfs()
函数返回的是包含root在内的单边最大路径和。分为三种情况:- 路径停在当前子树的根节点,在这个子树中收益:root.val
- 走入左子树,在这个子树中的最大收益:root.val + dfs(root.left)
- 走入右子树,在这个子树中的最大收益:root.val + dfs(root.right)
- 定义dfs函数:返回root的左(或右)子树能向root节点“提供”的最大路径和。即,一条从父节点延伸下来的路径,能在当前子树中获得的最大收益。也就是:
- 若root的左节点对应的最大路径和是负数(在拖后腿,所以抛弃),则最大路径和为0,因此有
l=max(0,dfs(root.left))
。右节点同理 - 记得写递归结束的条件
Q:为什么递归函数返回的是以根节点为首的单边最大路径和,而不是两边最大路径和?
A:若以1为根节点,它对应的最大路径和当然是1+2+3。但对于9而言,1作为9的左子树,1节点只能提供1+3(单边最大路径和),而不能提供1+2+3,否则1+2+3+9会产生路径重复。因此递归函数要返回单边最大路径和,而递归过程中不断更新全局最大路径和self.maxVal=max(self.maxVal,root.val+l+r)
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def maxPathSum(self, root):
"""
:type root: TreeNode
:rtype: int
"""
def dfs(root):
if not root:return 0
# 后序遍历:左
l=max(0,dfs(root.left))
# 后序遍历:右
r=max(0,dfs(root.right))
# 后序遍历:中,实现最大值的更新
self.maxVal=max(self.maxVal,root.val+l+r)
# 递归函数返回: 从根节点延生下来的一条路径
return root.val+max(l,r)
self.maxVal=float("-inf")
dfs(root)
return self.maxVal
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def __init__(self):
self.ans = float("-inf")
def help(self,root):
if not root: return 0
l=max(0,self.help(root.left))
r=max(0,self.help(root.right))
self.ans=max(self.ans, root.val + l + r)
return root.val+max(l,r)
def maxPathSum(self, root):
"""
:type root: TreeNode
:rtype: int
"""
# self.ans=float("-inf")
self.help(root)
return self.ans
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
// 定义全局变量
int maxVal = INT_MIN;
//dfs函数返回某个子树+当前节点对应的 单边最大和
int dfs(TreeNode *root) {
if (root == nullptr)
return 0;
int l = max(0, dfs(root->left));
int r = max(0, dfs(root->right));
maxVal = max(maxVal, root->val + l + r);
return root->val + max(l, r);
}
int maxPathSum(TreeNode *root) {
dfs(root);
return maxVal;
}
};
类似题目104. 二叉树的最大深度_后序遍历
dfs()
函数返回包含root
在内的二叉树最大深度
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def maxDepth(self, root):
"""
:type root: TreeNode
:rtype: int
"""
def dfs(root):
if not root: return 0
l=dfs(root.left)
r=dfs(root.right)
return max(l,r)+1
return dfs(root)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int dfs(TreeNode *root) {
if (root == nullptr)
return 0;
int l = dfs(root->left);
int r = dfs(root->right);
ans = max(l, r) + 1;
return ans;
}
int ans = 0;
int maxDepth(TreeNode *root) {
return dfs(root);
}
};
//或者直接写:
class Solution {
public:
int maxDepth(TreeNode *root) {
if (root == nullptr)
return 0;
int l = maxDepth(root->left);
int r = maxDepth(root->right);
return max(l, r) + 1;
}
};
105. 从前序与中序遍历序列构造二叉树_前序遍历
树的前序遍历:先构造根节点,基于根节点的信息找到左右子树的preorder和inorder范围,然后分别处理左右子树
- 前序遍历的第一个元素是根节点
- 在中序遍历中找到根节点的位置,其左边的元素就是左子树,右边的元素就是右子树
- 然后递归处理左右子树
- 注意写上递归结束条件(左右子树为空时)
- 前序数组怎么切分呢?前序数组的 (左子树部分+根节点) 长度 和 中序数组的 (左子树部分+根节点) 长度是一样的
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def buildTree(self, preorder, inorder):
"""
:type preorder: List[int]
:type inorder: List[int]
:rtype: TreeNode
"""
if not (preorder and inorder):return None
root=TreeNode(preorder[0])# 前序遍历:根 构造根节点
midIdx=inorder.index(root.val)
# 递归处理左右子树
root.left=self.buildTree(preorder[1:midIdx+1],inorder[:midIdx])# 前序遍历:左
root.right=self.buildTree(preorder[midIdx+1:],inorder[midIdx+1:])# 前序遍历:右
return root
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (preorder.size() == 0 ||inorder.size()==0)
return NULL;
TreeNode *root=new TreeNode(preorder[0]);
int midIdx=0;
while (inorder[midIdx]!=root->val)
midIdx++;
vector<int> preorder_l(preorder.begin()+1,preorder.begin()+midIdx+1);
vector<int> preorder_r(preorder.begin()+midIdx+1,preorder.end());
vector<int> inorder_l(inorder.begin(),inorder.begin()+midIdx+1);
vector<int> inorder_r(inorder.begin()+midIdx+1,inorder.end());
root->left= buildTree(preorder_l,inorder_l);
root->right= buildTree(preorder_r,inorder_r);
return root;
}
};
99. 恢复二叉搜索树_中序遍历
题目说二叉搜索树,中序遍历后应该是升序排列。否则若出现非递增的元素,说明这个地方有错误
- 先中序遍历二叉搜索树,保存在nodes列表中(列表中的元素是节点)
- pre保存遍历过程中i的前一个节点。如果发现异常,标定出
- 找到错误节点x、y后,交换x与y的值
方法1:递归
- 不要跳进递归,定义递归函数的意义并相信它能实现自己的功能 (注意加上递归结束的条件)
- 递归函数的功能:记录异常点出现的位置,并且更新pre,使得pre始终在root前面
- 中序遍历左;
- 中序遍历中:找到两个变化的点,第一个点赋值pre,第二个点赋值root。及时记录并更新pre(这样,在后面的中序遍历右时,pre的值为根节点的值,可以进行新一轮的递归和比较)
- 中序遍历右
- 交换两个点的数值
注意!!!!!!
if self.pre!=None: self.pre=None
在dfs函数中,一定要判断
if self.pre!=None:
,(原因)为了能够递归起来,需要确保self.pre!=None(因为初始化 self.pre=None)
否则在执行
if self.fir == None and self.pre.val >= root.val:
时,编译器都不确定self.pre
成不成立,更别说self.pre.val
了;
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def recoverTree(self, root):
"""
:type root: TreeNode
:rtype: None Do not return anything, modify root in-place instead.
"""
# 中序遍历
def dfs(root):
if not root: return
# 中序遍历:左
dfs(root.left)
# 中序遍历:中
# 找到两个左大右小的地方,第一个地方把pre赋给fir,第二个地方把root赋给sec
if self.pre!=None: # 在递归过程中,pre不断进行更新,始终指向当前节点的前一个(不是物理空间的前面,是中序遍历顺序的前面)节点
if self.fir == None and self.pre.val >= root.val:
self.fir = self.pre
if self.fir != None and self.pre.val >= root.val:
self.sec = root
# pre为root前面(不是物理空间的前面,是中序遍历顺序的前面)的节点
# 在下次进入dfs(root)入口前,及时记录并更新pre
self.pre = root
# 中序遍历:右
dfs(root.right)
self.fir = None
self.sec = None
self.pre=None
dfs(root)
# 交换两个地方的值
self.fir.val,self.sec.val=self.sec.val,self.fir.val
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode *fir = nullptr;
TreeNode *sec = nullptr;
TreeNode *pre = nullptr;
void dfs(TreeNode *root) {
// 递归结束的条件
if (!root)
return;
// 中序遍历:左
dfs(root->left);
// 中序遍历:中
if (pre != nullptr) {
if (fir == nullptr && pre->val >= root->val) fir = pre;
if (fir != nullptr && pre->val >= root->val) sec = root;
}
pre = root;
// 中序遍历:右
dfs(root->right);
}
void recoverTree(TreeNode *root) {
dfs(root);
int tmp = fir->val;
fir->val = sec->val;
sec->val = tmp;
}
};
P.S., 以下供参考:
- 在测试用例不严格的情况下,可以对pre赋一个最小的值,然后在dfs中对其更新,这样就不用判断pre是否合法了
- 在python中可以通过
- 在c++中,由于有个测试用例中包括了
INT_MIN
,所以会报错,可以把pre 置为空指针# Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution(object): def recoverTree(self, root): """ :type root: TreeNode :rtype: None Do not return anything, modify root in-place instead. """ self.fir=None self.sec=None self.pre=TreeNode(float("-inf")) def dfs(root): if not root: return dfs(root.left) if self.fir==None and self.pre.val>root.val: self.fir=self.pre if self.fir and self.pre.val>=root.val: self.sec=root self.pre=root dfs(root.right) dfs(root) self.fir.val,self.sec.val=self.sec.val,self.fir.val
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: TreeNode *fir = nullptr; TreeNode *sec = nullptr; TreeNode *pre = new TreeNode(INT_MIN); void dfs(TreeNode *root) { // 递归结束的条件 if (!root) return; // 中序遍历:左 dfs(root->left); // 中序遍历:中 if (fir == nullptr && pre->val >= root->val) fir = pre; if (fir != nullptr && pre->val >= root->val) sec = root; // 也可以这样 //if (pre->val >= root->val) { // fir = (fir == nullptr) ? pre : fir; // sec = root; // } pre = root; // 中序遍历:右 dfs(root->right); } void recoverTree(TreeNode *root) { dfs(root); int tmp = fir->val; fir->val = sec->val; sec->val = tmp; } };
方法2:遍历一次树,保存到数组中,再进行判断
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def recoverTree(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
nodes=[]# nodes 中保存节点
# 中序遍历二叉树,得到递增序列
def dfs(root):
if not root: return
dfs(root.left)
nodes.append(root)
dfs(root.right)
dfs(root)
# 如果出现非递增的元素,说明这个地方有错误
x=None
y=None
pre=nodes[0]
for i in range(1,len(nodes)):
if pre.val>nodes[i].val:
y=nodes[i]
if not x:
x=pre
pre=nodes[i]
# 交换x与y的值
if x and y:
x.val,y.val=y.val,x.val
或者
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def recoverTree(self, root):
"""
:type root: TreeNode
:rtype: None Do not return anything, modify root in-place instead.
"""
node = []
# 中序遍历把节点保存在列表中
def dfs(root):
if not root: return
dfs(root.left)
node.append(root)
dfs(root.right)
dfs(root)
pre = node[0]
n = len(node)
x = None
y = None
for i in range(1, n):
# 第一次左大右小,保存左
if (not x) and pre.val > node[i].val:
x = pre
# 第二次左大右小,保存右
if x and pre.val > node[i].val:
y = node[i]
pre = node[i]
# 交换值
if x and y:
x.val, y.val = y.val, x.val
注意!!!!!!
TreeNode *fir = nullptr;
和TreeNode *sec = nullptr;
一定要赋初值nullptr,否则指针会乱指- 传到dfs中的是nodes的引用 (当然也可以把nodes置为全局变量)
class Solution {
public:
// 注意这里传的是nodes的引用
void dfs(TreeNode *root, vector<TreeNode *> &nodes) {
if (root == NULL)
return;
dfs(root->left, nodes);
nodes.push_back(root);
dfs(root->right, nodes);
}
void recoverTree(TreeNode *root) {
vector < TreeNode * > nodes;
dfs(root, nodes);
//这里要给指针赋初值,否则它会乱指到其他地方
TreeNode *fir = nullptr;
TreeNode *sec = nullptr;
for (int i = 0; i < nodes.size() - 1; ++i) {
if (nodes[i]->val > nodes[i + 1]->val) {
sec = nodes[i + 1];
if (fir == NULL) {
fir = nodes[i];
}
}
}
if (fir != nullptr && sec != nullptr) {
int tmp = fir->val;
fir->val = sec->val;
sec->val = tmp;
}
}
};
其他写法:
class Solution {
public:
vector<TreeNode *> nodes;
void dfs(TreeNode *root) {
if (root == NULL)
return;
dfs(root->left);
nodes.push_back(root);
dfs(root->right);
}
void recoverTree(TreeNode *root) {
dfs(root);
TreeNode *fir= nullptr;
TreeNode *sec= nullptr;
// TreeNode *pre = new TreeNode(INT_MIN);
TreeNode *pre= nullptr;
for (int i = 0; i < nodes.size(); i++) {
if (pre){
if (pre->val >= nodes[i]->val) {
sec = nodes[i];
if (!fir)
fir = pre;
}}
pre = nodes[i];
}
if (fir != nullptr && sec != nullptr) {
int tmp = fir->val;
fir->val = sec->val;
sec->val = tmp;}
}
};