持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情
124. 二叉树中的最大路径和
思路
(递归,树的遍历) O(n^2)
路径
在这道题目中,路径是指从树中某个节点开始,沿着树中的边走,走到某个节点为止,路过的所有节点的集合。路径的权值和是指路径中所有节点的权值的总和。
对于一棵树,我们可以将其划分为很多的子树,如下图所示,虚线矩形围起来的子树。我们把这颗子树的蓝色节点称为该子树最高节点。用最高节点可以将整条路径分为两部分:从该节点向左子树延伸的路径,和从该节点向右子树延伸的部分。
如图所示:
我们可以递归遍历整棵树,递归时维护从每个子树从最高节点开始往下延伸的最大路径和。
- 对于每个子树的最高节点,递归计算完左右子树后,我们将左右子树维护的两条最大路径,和该点拼接起来,就可以得到以这个点为最高节点子树的最大路径。(这条路径一定是:左子树路径->最高节点->右子树路径)
- 然后维护从这个点往下延伸的最大路径:从左右子树的路径中选择权值大的一条延伸即可。(只能从左右子树之间选一条路径)
最后整颗树的最大路径和为: 根节点值+左子树最大路径和+右子树最大路径和,即left_max + right_max + root->val
注意:
如果某条路径之和小于0,那么我们选择不走该条路径,因此其路径之和应和0之间取最大值。
时间复杂度分析: 每个节点仅会遍历一次,所以时间复杂度是 O(n)。
c++代码
/**
* 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 res = INT_MIN;
int maxPathSum(TreeNode* root) {
dfs(root);
return res;
}
int dfs(TreeNode* root){ //root到叶节点的最大路径和
if(!root) return 0;
int left = max(0, dfs(root->left)), right = max(0, dfs(root->right));
res = max(res, root->val + left + right);
return root->val + max(left, right);
}
};
128. 最长连续序列
思路
(哈希) O(n)
在一个未排序的整数数组 nums中 ,找出最长的数字连续序列,朴素的做法是:枚举nums中的每一个数x,并以x起点,在nums数组中查询x + 1,x + 2,,,x + y是否存在。假设查询到了 x + y,那么长度即为 y - x + 1,不断枚举更新答案即可。
如果每次查询一个数都要遍历一遍nums数组的话,时间复杂度为O(n) ,其实我们可以用一个哈希表来存贮数组中的数,这样查询的时间就能优化为O(1) 。
数组哈希去重
为了保证O(n)的时间复杂度,避免重复枚举一段序列,我们要从序列的起始数字向后枚举。也就是说如果有一个x, x+1, x+2,,,, x+y的连续序列,我们只会以x为起点向后枚举,而不会从x+1,x+2,,,向后枚举。
如何每次只枚举连续序列的起始数字x?
其实只需要每次在哈希表中检查是否存在 x − 1即可。如果x - 1存在,说明当前数x不是连续序列的起始数字,我们跳过这个数。
具体过程如下:
- 1、定义一个哈希表
hash,将nums数组中的数都放入哈希表中。 - 2、遍历哈希表
hash,如果当前数x的前驱x-1不存在,我们就以当前数x为起点向后枚举。 - 3、假设最长枚举到了数
y,那么连续序列长度即为y-x+1。 - 4、不断枚举更新答案。
时间复杂度分析: while循环最多执行n次,因此时间复杂度为O(n) 。
c++代码
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> hash;
for(int x : nums) hash.insert(x);
int res = 0;
for(int x : hash){
if(!hash.count(x - 1)){
int y = x;
while(hash.count(y + 1)) y++;
res = max(res, y - x + 1);
}
}
return res;
}
};
136. 只出现一次的数字
思路
(位运算) O(n)
异或运算有以下三个性质:
- 1、
0^0 = 0,0^1 = 1,0异或任何数=任何数 - 2、
1^0 = 1,1^1 = 0,1异或任何数 = 任何数取反 - 3、任何数异或自己=把自己置
0,即a^a=0
因此这道题可以用位运算来做,过程如下:
- 1、两个相同的元素经过异或之后会变为
0。 - 2、将数组所有元素异或在一起即可得到出现
1次的元素值。
时间复杂度分析: O(n),其中 n 是数组长度。
c++代码
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = 0;
for(int x : nums){
res ^= x;
}
return res;
}
};
\