LeetCode 热题 HOT 100 打卡计划 | 第十六天 | 每日进步一点点

153 阅读4分钟

图片.png

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情

124. 二叉树中的最大路径和

思路

(递归,树的遍历) O(n^2)

路径

在这道题目中,路径是指从树中某个节点开始,沿着树中的边走,走到某个节点为止,路过的所有节点的集合。路径的权值和是指路径中所有节点的权值的总和。

对于一棵树,我们可以将其划分为很多的子树,如下图所示,虚线矩形围起来的子树。我们把这颗子树的蓝色节点称为该子树最高节点。用最高节点可以将整条路径分为两部分:从该节点向左子树延伸的路径,和从该节点向右子树延伸的部分。

如图所示:

图片.png

我们可以递归遍历整棵树,递归时维护从每个子树从最高节点开始往下延伸的最大路径和。

  • 对于每个子树的最高节点,递归计算完左右子树后,我们将左右子树维护的两条最大路径,和该点拼接起来,就可以得到以这个点为最高节点子树的最大路径。(这条路径一定是:左子树路径->最高节点->右子树路径
  • 然后维护从这个点往下延伸的最大路径:从左右子树的路径中选择权值大的一条延伸即可。(只能从左右子树之间选一条路径

最后整颗树的最大路径和为: 根节点值+左子树最大路径和+右子树最大路径和,即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) 。

数组哈希去重

图片.png

为了保证O(n)的时间复杂度,避免重复枚举一段序列,我们要从序列的起始数字向后枚举。也就是说如果有一个x, x+1, x+2,,,, x+y的连续序列,我们只会以x为起点向后枚举,而不会从x+1,x+2,,,向后枚举。

图片.png

如何每次只枚举连续序列的起始数字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 = 00^1 = 10异或任何数=任何数
  • 2、1^0 = 11^1 = 01异或任何数 = 任何数取反
  • 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;
     }
 };

\