Leetcode 第426场周赛分析总结

在这里插入图片描述

3370. 仅含置位位的最小整数

AC代码

class Solution {
public:int smallestNumber(int n) {int x = 1;while (x - 1 < n) {x <<= 1;}return x - 1;}
};

分析总结

也可以先直接获取n的长度,然后计算得到,这样时间复杂度由O(logn)优化为O(1)

在C++20中可以使用头文件中的std::bit_width函数,传入一个无符号整数,会返回其所需的最小位数(也就是最高位1的位数)

如果题目要求大于n的数,我的第一思路是判断n是不是2的幂次减1,分类讨论。灵神说不必如此,相当于我们已经拥有了求大于等于n的能力,那么对于大于n的情况,我们只需要求大于等于n+1,即在整数域上:> x相当于 >= x + 1,这是一种思维的转换能力。

class Solution {
public:int smallestNumber(int n) {return (1 << std::bit_width(static_cast<unsigned>(n))) - 1;}
};

3371. 识别数组中的最大异常值

AC代码

class Solution {
public:int getLargestOutlier(vector<int>& nums) {int n = nums.size();ranges::sort(nums);int sum = reduce(nums.begin(), nums.end());auto check = [&](int idx) -> bool {//[0, idx) [idx + 1, n)int seg_sum = sum - nums[idx];if (seg_sum % 2 != 0) return false;int target_sum = seg_sum / 2;int l = lower_bound(nums.begin(), nums.end(), target_sum) - nums.begin();int r = upper_bound(nums.begin(), nums.end(), target_sum) - nums.begin();//[l, r)if (l < r) {if (r <= idx || l > idx || r - l > 1 || l != idx) return true;else return false;} else {return false;}};for (int i = n - 1; i >= 0; --i) {if (check(i)) {return nums[i];break;}}return nums[0];}
};

分析总结

比赛过程中,我的第一想法是,整个数组的和等于异常值和剩下数字的和以及剩下的数字组成,那么我可以遍历异常值,判断剩下的数字是否满足情况,由于要快速判断一个数字是否存在,又忘记了使用哈希表,所以就先进行排序,从大往小遍历异常值,时间复杂度是O(nlogn),空间复杂度O(1)。当时能够很快AC我还有点开心,觉得还是有点复杂的。

听了灵神的解析才发现自己马达马达达内。最重要的一点是判断一个数字是否出现应该考虑使用哈希表加速,其次我也没有进行数学抽象分析。要是考虑到哈希表就不会写出这么复杂的代码,通过复杂的二分判断数字是否存在。

class Solution {
public:int getLargestOutlier(vector<int>& nums) {int n = nums.size();int sum = 0;unordered_map<int, int> cnt;for (auto x : nums) {sum += x;++cnt[x];}int ans = numeric_limits<int>::min();for (auto x : nums) {--cnt[x];int t = sum - 2 * x;if (cnt[t]) {ans = max(ans, t);}++cnt[x];}return ans;}
};

灵神留了一个思考题,考虑当数组已经有序的情况下如何使用空间复杂度为O(1)的算法解决问题。假设特殊数字的和是 s s s,异常值是 x x x,数组的和是 s u m sum sum,那么问题就是要找到 2 s + x = s u m 2s + x =sum 2s+x=sum x x x,要使得 x x x尽可能大,对于升序数组,我们从右到左遍历 x x x,从左往右遍历 s s s

  • 如果此时 2 s + x > s u m 2s+x>sum 2s+x>sum,则剩下的 s s s(只会更大)都不满足情况,可以将 x x x向左移动;
  • 如果此时 2 s + x < s u m 2s+x<sum 2s+x<sum,则剩下的 x x x(只会更小)都不满足情况,可以将 s s s向右移动;

我本以为这就是一个简单的双指针,于是写了一下结果WA了。苦思冥想之际我发现,这个问题和普通的有序数组两数之和不同的地方在于,这里没有对称性。在两数之和中, x + y > t a r g e t x+y>target x+y>target y + x > t a r g e t y+x>target y+x>target是对称的,因此当两个指针相遇的时候我们可以判断数组中没有满足条件的数。但是对于这里而言, 2 s + x > s u m 2s+x>sum 2s+x>sum不代表 s + 2 x > s u m s+2x>sum s+2x>sum,因此即使两个指针相遇仍然需要继续移动。难点就在于当指针相遇的时候应该移动哪个指针呢?

要是两个指针相遇的时候不等于 s u m sum sum,我们仍然可以按照上面的规则移动。可是如果当相遇的时候等于 s u m sum sum,应该移动哪一个指针呢?实践发现,移动右指针可以AC,移动左指针就会WA,我觉得AC可能是数据的缘故,并不表示正确性。

class Solution {
public:int getLargestOutlier(vector<int>& nums) {int n = nums.size();int sum = std::reduce(nums.begin(), nums.end());sort(nums.begin(), nums.end());int l = 0, r = n - 1;while (l < n && r >= 0) {if (l == r) {--r;}int t = 2 * nums[l] + nums[r];if (t > sum) {--r;} else if (t < sum) {++l;} else {return nums[r];}}return -1;}
};

因此,我觉得对于这种没有对称性的情况,即使存在单调性,也并不适合使用双指针。

3372. 连接两棵树后最大目标节点数目 I

题目要求树上距离每个节点小于等于k的节点数目,在计算之前我们可以添加一个连边将树1和树2连接起来。经过分析不难发现这个连边方案是确定的:

  • 树1上的点肯定是当前计算的目标节点,因为从其他节点出发只会导致距离目标节点更远
  • 树2上的点应该是一个距离它小于等于k-1的节点数目最大的节点

那么问题就变成了,我们如何快速的计算树上一个节点到其他节点的距离。如果直接从某一个节点出发进行DFS(也可以BFS,不过使用DFS编码更简单,因为可以直接使用函数栈存储信息),时间复杂度是O(n),由于需要对每个节点计算,总的时间复杂度是 O ( n 2 + m 2 ) O(n^2+m^2) O(n2+m2)

class Solution {
public:vector<int> maxTargetNodes(vector<vector<int>>& edges1, vector<vector<int>>& edges2, int k) {auto get_dfs = [](decltype(edges1) edges) -> auto {int n = edges.size() + 1;vector graph(n, vector<int>());for (auto &&edge : edges) {auto u = edge[0], v = edge[1];graph[u].push_back(v);graph[v].push_back(u);}function<int(int, int, int)> dfs;dfs = [graph = std::move(graph), &dfs](int u, int fa, int d) -> int {if (d < 0) return 0;int ans = 1;for (auto v : graph[u]) {if (v == fa) continue;ans += dfs(v, u, d - 1);}return ans;};return dfs;};int maxn2 = 0;if (k > 0) {auto dfs = get_dfs(edges2);int n = edges2.size() + 1;for (int i = 0; i < n; ++i) {maxn2 = max(maxn2, dfs(i, -1, k - 1));}}auto dfs = get_dfs(edges1);int n = edges1.size() + 1;vector<int> ans(n, 0);for (int i = 0; i < n; ++i) {ans[i] = dfs(i, -1, k) + maxn2;}return ans;}
};

这里我想模仿灵神在视频中提到的闭包的技巧,这样可以将建图和dfs的逻辑绑定起来,避免在调用的时候传递参数。实际在实现的时候我有些疑惑到底怎样在闭包函数中捕获局部变量和自身。在动态内存分配的语言中,我们不需要考虑捕获,只要直接使用,就会有C++中引用捕获的语义,而语言自身通过引用计数保证了局部变量的内存不会被错误的释放。可是在C++中这一切都是确定性的,没有额外的垃圾回收,需要我们自己保证内存的安全性,就必须要考虑捕获的变量生存周期。

对于普通的局部变量,我们肯定不能进行引用捕获,当get_dfs函数返回后,dfs捕获的引用将会失效,因此这里我们使用移动捕获,通过移动拷贝将局部变量保存在dfs函数对象中。

对于需要返回的函数对象本身,因为我们需要在lambda表达式中访问自身,所以也需要捕获。可是如果进行值捕获的话,我们在初始化dfs的过程中dfs本身是一个不完全类型,是不能拷贝的(实际测试值捕获dfs会报错),因此只能进行引用捕获。但是捕获一个局部变量(dfs本身作为局部变量)不会造成悬挂引用吗?实际上的确不会,原因是返回值作为一个特殊的局部变量其实是保存在上一层函数栈中,不会随着函数的调用结束而释放内存,具体的细节和RVO(返回值优化)等C++底层技术和编译原理有关。

分析总结

比赛中最终这道题还是没有做出来,觉得不应该。其实核心的思路当时已经思考清楚了,但是在考虑实现计算距离每个节点不超过k的节点数目时,我想着应该使用一种时间复杂度更优秀的做法,因为对于每个节点都要计算,直接简单对每个节点遍历好像会超时。但实际上当时没有认真看数据范围,只是凭感觉会超时(要是问我为什么,只能说比赛的时候脑子抽了)。

想来想去好像没有更好的做法,就去问了一下GPT,它告诉我可以先以某个节点为根节点(例如0号节点)遍历树,得到每个节点的深度和父亲,然后再求解任意两个节点的最近公共祖先,然后任意两点之间的距离可以通过 d i s [ i ] [ j ] = d e p t h [ i ] + d e p t h [ j ] − 2 ∗ d e p t h [ l c a [ i ] [ j ] ] dis[i][j]=depth[i]+depth[j]-2*depth[lca[i][j]] dis[i][j]=depth[i]+depth[j]2depth[lca[i][j]]计算,我一听很有道理呀,然后就哼哧哼哧实现,结果一直报错。下来再看才发现求解lca的时候写错了,修改以后就对了。

class Solution {vector<vector<int>> buildGraph(const vector<vector<int>>& edges) {int n = edges.size() + 1;vector graph(n, vector<int>());for (auto &&edge : edges) {int u = edge[0], v = edge[1];graph[u].push_back(v);graph[v].push_back(u);}return graph;}vector<vector<int>> getDis(const vector<vector<int>>& graph) {int n = graph.size();vector dis(n, vector<int>(n, 0));vector depth(n, 0);vector father(n, -1);vector lca(n, vector<int>(n, -1));function<void(int, int, int)> dfs;dfs = [&](int u, int fa, int d) {father[u] = fa;depth[u] = d;for (auto v : graph[u]) {if (v == fa) continue;dfs(v, u, d + 1);}};dfs(0, -1, 0);father[0] = 0;/*for (int i = 0; i < n; ++i) {cout << "depth " << i << ": " << depth[i] << endl;}*/function<int(int, int)> get_lca;get_lca = [&](int u, int v) -> int {int ret = lca[u][v];if (ret != -1) return ret;if (depth[u] > depth[v]) {ret = get_lca(father[u], v);} else if (depth[u] < depth[v]) {ret = get_lca(father[v], u);} else {if (u == v) {ret = u;} else {ret = get_lca(father[u], father[v]);}}lca[u][v] = ret;lca[v][u] = ret;return ret;};for (int i = 0; i < n; ++i) {dis[i][0] = 1;//(i, i)for (int j = i + 1; j < n; ++j) {int d = depth[i] + depth[j] - 2 * depth[get_lca(i, j)];//cout << depth[i] << " " << depth[j] << " " <<depth[get_lca(i, j)] << endl;//cout << i << "---" << j << " : " << d << endl;dis[i][d]++;dis[j][d]++;}}return dis;}
public:vector<int> maxTargetNodes(vector<vector<int>>& edges1, vector<vector<int>>& edges2, int k) {auto graph1 = buildGraph(edges1);auto graph2 = buildGraph(edges2);int n = graph1.size();int m = graph2.size();vector ans(n, 0);auto dis1 = getDis(graph1);auto dis2 = getDis(graph2);int max_target2 = [&]() -> int {int t = min(m, k);int ret = -1;for (int i = 0; i < m; ++i) {ret = max(ret, reduce(dis2[i].begin(), dis2[i].begin() + t));}return ret;}();//cout << "max_target2 = " << max_target2 << endl;int t = min(n, k + 1);for (int i = 0; i < n; ++i) {ans[i] = reduce(dis1[i].begin(), dis1[i].begin() + t) + max_target2;}return ans;}
};

求解lca的时候用了记忆化。实际上认真思考这种方式的时间复杂度也是 O ( n 2 ) O(n^2) O(n2)的,甚至常数更大。

比赛的时候编码想着把建图和搜索的逻辑封装起来,通过参数传递。与闭包的方式比起来,这种方式耦合度更低,但是对于这个具体的场景会有参数传递的麻烦,可见编码的高级技巧可以简化工作量,闭包函数作为一种比较高级的编码技巧我还是不太熟悉。

另一方面也暴露出我对树上搜索还是不够熟悉,这种简单的操作本来应该直接写出来的。

3373. 连接两棵树后最大目标节点数目 II

这道题比赛的时候完全没有看,觉得主要的难点相比第三题在于,意识到一个树上距离某个节点距离为偶数的所有节点形成的集合互相满足距离为偶数,剩下的节点形成了另一个互相满足距离为偶数的集合。

那么在树1上,答案就固定的分成两种,我们只需要对节点进行染色分类,记录每种颜色节点的数目和每个节点的颜色。剩下的问题是如何和树2进行连边:

  • 我们可以从距离树1目标节点x为偶数(例如从x出发)连边,目标就是找到树2上距离为奇数的节点数目最大的节点
  • 也可以从距离树1目标节点x为奇数(例如从x的某个相连的节点出发)连边,目标就是找到树2上距离为偶数的节点数目最大的节点

无论哪种情况,这个数目都是固定的,和x的选取没有关系,其实就是两种颜色集合大小的较大值。

class Solution {
public:vector<int> maxTargetNodes(vector<vector<int>>& edges1, vector<vector<int>>& edges2) {auto aux = [](decltype(edges1) edges) -> pair<vector<int>, vector<int>> {int n = edges.size() + 1;vector graph(n, vector<int>());for (auto &&edge : edges) {int u = edge[0], v = edge[1];graph[u].push_back(v);graph[v].push_back(u);}vector colors(n, -1);vector cnt(2, 0);function<void(int,int,int)> dfs;dfs = [&](int u, int fa, int c) {colors[u] = c;cnt[c]++;for (auto v : graph[u]) {if (v == fa) continue;dfs(v, u, c ^ 1);}};dfs(0, -1, 0);return {colors, cnt};};auto [colors1, cnt1] = aux(edges1);auto [colors2, cnt2] = aux(edges2);int maxn2 = max(cnt2[0], cnt2[1]);int n1 = edges1.size() + 1;vector ans(n1, 0);for (int i = 0; i < n1; ++i) {ans[i] = cnt1[colors1[i]] + maxn2;}return ans;}
};

分析总结

看了一下灵神的C++实现,基本上相同,不过我用一个数组保存了每个节点的染色信息,所以不需要第二次BFS。

虽然实际写出来后觉得没有想象中那么难,但是关键的思路转换,以及在时间较为紧张的情况下保持清晰的头脑可能才是更重要的能力,这种能力需要不断在实践中磨练。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/501549.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在 SQL 中,区分 聚合列 和 非聚合列(nonaggregated column)

文章目录 1. 什么是聚合列&#xff1f;2. 什么是非聚合列&#xff1f;3. 在 GROUP BY 查询中的非聚合列问题示例解决方案 4. 为什么 only_full_group_by 要求非聚合列出现在 GROUP BY 中&#xff1f;5. 如何判断一个列是聚合列还是非聚合列&#xff1f;6. 总结 在 SQL 中&#…

C++ —— 智能指针

内存泄漏 什么是内存泄漏&#xff0c;内存泄漏的危害 什么是内存泄漏&#xff1a;内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内 存泄漏并不是指内存在物理上的消失&#xff0c;而是应用程序分配某段内存后&#xff0c;因为设计错误&#xff0c;失去…

Ethernet 系列(12)-- 基础学习::SOME/IP

目录 1. SOME/IP简介&#xff1a; 1.1 什么是SOME/IP&#xff1a; 1.2 什么时候使用SOME/IP&#xff1a; 2. SOME/IP的特点&#xff1a; 2.1 序列化&#xff1a; 2.2 远程过程调用&#xff08;RPC&#xff09;: 2.3 服务发现&#xff1a; 2.4 发布/订阅&#xff1a; 2.5 UDP消息…

UE5.3 虚幻引擎 Windows插件开发打包(带源码插件打包、无源码插件打包)

0 引言 随着项目体量的增大&#xff0c;所有代码功能都放一起很难管理。所以有什么办法可以将大模块划分成一个个小模块吗。当然有&#xff0c;因为虚幻引擎本身就遇到过这个问题&#xff0c;他的解决办法就是使用插件的形式开发。 例如&#xff0c;一个团队开发了文件I/O模块插…

自学记录鸿蒙API 13:实现多目标识别Object Detection

起步&#xff1a;什么叫多目标识别&#xff1f; 无论是生活中的动物识别、智能相册中的场景分类&#xff0c;还是工业领域的检测任务&#xff0c;都能看到多目标识别的身影。这次&#xff0c;我决定通过学习HarmonyOS最新的Object Detection API&#xff08;API 13&#xff09…

光伏安装在屋顶:安全、环保还是潜在威胁?

随着环保意识的增强和科技的进步&#xff0c;光伏发电作为一种可再生能源技术&#xff0c;正逐渐走进千家万户。然而&#xff0c;随着光伏板的普及&#xff0c;关于其在屋顶安装是否对人体有害的疑问也随之而来。 一、光伏发电的基本原理 光伏发电是利用半导体界面的光生伏特效…

被催更了,2025元旦源码继续免费送

“时间从来不会停下&#xff0c;它只会匆匆流逝。抓住每一刻&#xff0c;我们才不会辜负自己。” 联系作者免费领&#x1f496;源&#x1f496;码。 三联支持&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;欢迎留言讨论 更多内容敬请期待。如有需要源码可以联系作者免…

MYsql--------ubantu中安装mysql

在Ubuntu平台上下载、启动和关闭MySQL的方法如下&#xff1a; 下载安装MySQL 更新软件包列表&#xff1a;打开终端&#xff0c;输入以下命令&#xff0c;确保软件包列表是最新的。sudo apt update安装MySQL服务器&#xff1a;执行以下命令安装MySQL服务器。在安装过程中&…

pygame飞机大战

飞机大战 1.main类2.配置类3.游戏主类4.游戏资源类5.资源下载6.游戏效果 1.main类 启动游戏。 from MainWindow import MainWindow if __name__ __main__:appMainWindow()app.run()2.配置类 该类主要存放游戏的各种设置参数。 #窗口尺寸 import random import pygame WIND…

Flutter中的网络请求图片存储为缓存,与定制删除本地缓存

Flutter中的网络请求图片存储为缓存&#xff0c;与定制删除本地缓存 1&#xff1a;封装请求图片函数 2&#xff1a;访问的图片都会转为本地缓存&#xff0c;当相同的请求url&#xff0c;会在本地调用图片 3&#xff1a;本地缓存管理【windows与andriod已经测试】【有页面】【有…

无线AP安装注意事项

现在的办公楼、酒店等项目中都设计含有网络无线覆盖这一项&#xff0c;在项目实施中&#xff0c;往往采用的是便捷并且后期便于网络无线设备管理的无线ap设备&#xff0c;作为前端无线信号的覆盖。在具体安装无线AP过程中&#xff0c;我们必须要注意以下几点才能保证项目实施完…

Golang的容器编排实践

Golang的容器编排实践 一、Golang中的容器编排概述 作为一种高效的编程语言&#xff0c;其在容器编排领域也有着广泛的运用。容器编排是指利用自动化工具对容器化的应用进行部署、管理和扩展的过程&#xff0c;典型的容器编排工具包括Docker Swarm、Kubernetes等。在Golang中&a…

计算机毕业设计Django+Tensorflow音乐推荐系统 音乐可视化 卷积神经网络CNN LSTM音乐情感分析 机器学习 深度学习 Flask

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

C# 在PDF中添加和删除水印注释 (Watermark Annotation)

目录 使用工具 C# 在PDF文档中添加水印注释 C# 在PDF文档中删除水印注释 PDF中的水印注释是一种独特的注释类型&#xff0c;它通常以透明的文本或图片形式叠加在页面内容之上&#xff0c;为文档添加标识或信息提示。与传统的静态水印不同&#xff0c;水印注释并不会永久嵌入…

分析服务器 systemctl 启动gozero项目报错的解决方案

### 分析 systemctl start beisen.service 报错 在 Linux 系统中&#xff0c;systemctl 是管理系统和服务的主要工具。当我们尝试重启某个服务时&#xff0c;如果服务启动失败&#xff0c;systemctl 会输出错误信息&#xff0c;帮助我们诊断和解决问题。 本文将通过一个实际的…

Dubbo扩展点加载机制

加载机制中已经存在的一些关键注解&#xff0c;如SPI、©Adaptive> ©Activateo然后介绍整个加载机制中最核心的ExtensionLoader的工作流程及实现原理。最后介绍扩展中使用的类动态编译的实 现原理。 Java SPI Java 5 中的服务提供商https://docs.oracle.com/jav…

如何利用Logo设计免费生成器创建专业级Logo

在当今的商业世界中&#xff0c;一个好的Logo是品牌身份的象征&#xff0c;它承载着公司的形象与理念。设计一个专业级的Logo不再需要花费大量的金钱和时间&#xff0c;尤其是当我们拥有Logo设计免费生成器这样的工具时。接下来&#xff0c;让我们深入探讨如何利用这些工具来创…

游戏如何检测iOS越狱

不同于安卓的开源生态&#xff0c;iOS一直秉承着安全性更高的闭源生态&#xff0c;系统中的硬件、软件和服务会经过严格审核和测试&#xff0c;来保障安全性与稳定性。 据FairGurd观察&#xff0c;虽然iOS系统具备一定的安全性&#xff0c;但并非没有漏洞&#xff0c;如市面上…

智联视频超融合平台:电力行业的智能守护者

文章目录 一、远程实时监控与设备状态监测二、提高应急响应能力三、实现无人值守与减员增效四、保障电力设施安全与防范外部破坏五、提升电网运行管理效率与决策科学性六、助力电力企业数字化转型与智能化发展七、智联视频超融合平台 在当今数字化浪潮下&#xff0c;视频联网平…

卸载干净 IDEA(图文讲解)

目录 1、卸载 IDEA 程序 2、注册表清理 3、残留清理 1、卸载 IDEA 程序 点击屏幕左下角 Windows 图标 -> 设置-控制面板->intellij idea 勾选第一栏 Delete IntelliJ IDEA 2022.2 caches and local history&#xff0c;表示同时删除 IDEA 本地缓存以及历史。 Delete I…