当前位置: 首页 > news >正文

LeetCode 周赛上分之旅 #48 一道简单的树上动态规划问题

⭐️ 本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 和 BaguTree Pro 知识星球提问。

学习数据结构与算法的关键在于掌握问题背后的算法思维框架,你的思考越抽象,它能覆盖的问题域就越广,理解难度也更复杂。在这个专栏里,小彭与你分享每场 LeetCode 周赛的解题报告,一起体会上分之旅。

本文是 LeetCode 上分之旅系列的第 48 篇文章,往期回顾请移步到文章末尾~

LeetCode 双周赛 114

T1. 收集元素的最少操作次数(Easy)

  • 标签:模拟、散列表

T2. 使数组为空的最少操作次数(Medium)

  • 标签:贪心、散列表

T3. 将数组分割成最多数目的子数组(Medium)

  • 标签:思维、位运算

T4. 可以被 K 整除连通块的最大数目(Hard)

  • 标签:树上 DP


T1. 收集元素的最少操作次数(Easy)

https://leetcode.cn/problems/minimum-operations-to-collect-elements/description/

题解(散列表)

简单模拟题。

预初始化包含 1 − k 1 - k 1k 元素的集合,根据题意逆向遍历数组并从集合中移除元素,当集合为空时表示已经收集到所有元素,返回 n − i n - i ni

class Solution {fun minOperations(nums: List<Int>, k: Int): Int {val n = nums.sizeval set = (1..k).toHashSet()for (i in n - 1 downTo 0) {set.remove(nums[i])if (set.isEmpty()) return n - i}return -1}
}
class Solution:def minOperations(self, nums, k):n, nums_set = len(nums), set(range(1, k+1))for i in range(n-1, -1, -1):nums_set.discard(nums[i])if not nums_set:return n - ireturn -1
class Solution {
public:int minOperations(std::vector<int>& nums, int k) {int n = nums.size();unordered_set<int> set;for (int i = 1; i <= k; ++i) {set.insert(i);}for (int i = n - 1; i >= 0; --i) {set.erase(nums[i]);if (set.empty()) {return n - i;}}return -1;}
};
function minOperations(nums: number[], k: number): number {var n = nums.length;var set = new Set<number>();for (let i = 1; i <= k; ++i) {set.add(i);}for (let i = n - 1; i >= 0; --i) {set.delete(nums[i]);if (set.size === 0) {return n - i;}}return -1;
};
class Solution {int minOperations(List<int> nums, int k) {int n = nums.length;Set<int> set = Set<int>();for (int i = 1; i <= k; i++) {set.add(i);}for (int i = n - 1; i >= 0; i--) {set.remove(nums[i]);if (set.isEmpty) return n - i;}return -1;}
}

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n) 线性遍历;
  • 空间复杂度: O ( k ) O(k) O(k) 散列表空间。

T2. 使数组为空的最少操作次数(Medium)

https://leetcode.cn/problems/minimum-number-of-operations-to-make-array-empty/description/

题解(贪心)

题目两种操作的前提是数字相等,因此我们先统计每个元素的出现次数。

从最少次数的目标出发,显然能移除 3 3 3 个就尽量移除 3 3 3 个,再分类讨论:

  • 如果出现次数为 1 1 1,那么一定无解,返回 − 1 -1 1
  • 如果出现次数能够被 3 3 3 整除,那么操作 c n t / 3 cnt / 3 cnt/3 次是最优的;
  • 如果出现次数除 3 3 3 1 1 1,那么把 1 1 1 3 3 3 拆出来合并为 4,操作 c n t / 3 + 1 cnt / 3 + 1 cnt/3+1 次是最优的;
  • 如果出现次数除 3 3 3 2 2 2,那么剩下的 2 2 2 操作 1 1 1 次,即操作 c n t / 3 + 1 cnt / 3 + 1 cnt/3+1 次是最优的。

组合以上讨论:

class Solution {fun minOperations(nums: IntArray): Int {val cnts = HashMap<Int, Int>()for (e in nums) {cnts[e] = cnts.getOrDefault(e, 0) + 1}var ret = 0for ((_, cnt) in cnts) {if (cnt == 1) return -1when (cnt % 3) {0 -> {ret += cnt / 3}1, 2 -> {ret += cnt / 3 + 1}}}return ret}
}

继续挖掘题目特性,对于余数大于 0 0 0 的情况总是 向上取整 ,那么可以简化为:

class Solution {fun minOperations(nums: IntArray): Int {val cnts = HashMap<Int, Int>()for (e in nums) {cnts[e] = cnts.getOrDefault(e, 0) + 1}var ret = 0for ((_, cnt) in cnts) {if (cnt == 1) return -1ret += (cnt + 2) / 3 // 向上取整}return ret}
}
class Solution:def minOperations(self, nums: List[int]) -> int:cnts = Counter(nums)ret = 0for cnt in cnts.values():if cnt == 1: return -1ret += (cnt + 2) // 3return ret
class Solution {
public:int minOperations(std::vector<int>& nums) {unordered_map<int, int> cnts;for (auto &e : nums) {cnts[e] += 1;}int ret = 0;for (auto &p: cnts) {if (p.second == 1) return -1;ret += (p.second + 2) / 3;}return ret;}
};
function minOperations(nums: number[]): number {let cnts: Map<number, number> = new Map<number, number>();for (let e of nums) {cnts.set(e, (cnts.get(e) ?? 0) + 1);}let ret = 0;for (let [_, cnt] of cnts) {if (cnt == 1) return -1;ret += Math.ceil(cnt / 3);}return ret;
};
class Solution {int minOperations(List<int> nums) {Map<int, int> cnts = {};for (int e in nums) {cnts[e] = (cnts[e] ?? 0) + 1;}int ret = 0;for (int cnt in cnts.values) {if (cnt == 1) return -1;ret += (cnt + 2) ~/ 3; // 向上取整}return ret;}
}

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n) 线性遍历
  • 空间复杂度: O ( n ) O(n) O(n) 计数空间。

T3. 将数组分割成最多数目的子数组(Medium)

https://leetcode.cn/problems/split-array-into-maximum-number-of-subarrays/description/

题解(思维题)

一个重要的结论是:当按位与的数量增加时,按位与的结果是非递增的。

题目要求在子数组的按位与的和最小的前提下,让子数组的个数最大。根据上面的结论,显然将数组全部按位与是最小的。

分类讨论:

  • 如果整体按位于的结果不为 0 0 0,那么就不可能存在分割数组的方法使得按位与的和更小,直接返回 1 1 1
  • 否则,问题就变成分割数组的最大个数,使得每个子数组按位与为 0 0 0,直接贪心分割就好了。
class Solution {fun maxSubarrays(nums: IntArray): Int {val mn = nums.reduce { acc, it -> acc and it }if (mn > 0) return 1 // 特判var ret = 0var cur = Integer.MAX_VALUEfor (i in nums.indices) {cur = cur and nums[i]if (cur == 0) {cur = Integer.MAX_VALUEret++}}return ret }
}
class Solution:def maxSubarrays(self, nums: List[int]) -> int:if reduce(iand, nums): return 1ret, mask = 0, (1 << 20) - 1cur = maskfor num in nums:cur &= numif cur == 0: ret += 1; cur = maskreturn ret
class Solution {
public:int maxSubarrays(vector<int>& nums) {int mn = nums[0];for (auto num : nums) mn &= num;if (mn != 0) return 1;int ret = 0;int cur = INT_MAX;for (int i = 0; i < nums.size(); i++) {cur &= nums[i];if (cur == 0) {cur = INT_MAX;ret++;}}return ret;}
};
function maxSubarrays(nums: number[]): number {const n = nums.length;let mn = nums.reduce((acc, it) => acc & it);if (mn > 0) return 1; // 特判let mask = (1 << 20) - 1let ret = 0;let cur = mask;for (let i = 0; i < n; i++) {cur = cur & nums[i];if (cur === 0) {cur = mask;ret++;}}return ret;
};
class Solution {int maxSubarrays(List<int> nums) {var mn = nums.reduce((acc, it) => acc & it);if (mn > 0) return 1; // 特判var mask = (1 << 20) - 1;var ret = 0;var cur = mask;for (var i = 0; i < nums.length; i++) {cur = cur & nums[i];if (cur == 0) {cur = mask;ret++;}}return ret;}
}

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n) 线性遍历;
  • 空间复杂度: O ( 1 ) O(1) O(1) 仅使用常量级别空间。

T4. 可以被 K 整除连通块的最大数目(Hard)

https://leetcode.cn/problems/maximum-number-of-k-divisible-components/

问题分析

初步分析:

  • 问题目标: 求解分割后满足条件的最大连通块数量;
  • 问题条件: 连通块的和能够被 K 整除;
  • 关键信息: 题目保证数据是可以分割的,这是重要的前提。

思考实现:

在保证问题有解的情况下,树上的每个节点要么是单独的连通分量,要么与邻居组成连通分量。那么,这就是典型的「连或不连」和「连哪个」动态规划思维。

  • 思考「连或不连」:

如果节点 A A A 的价值能够被 K K K 整除,那么节点 A A A 能作为单独的连通分量吗?

不一定,例如 K = 3 K = 3 K=3 且树为 1 − 3 − 5 1 - 3 - 5 135 的情况,连通分量只能为 1 1 1,因为 3 3 3 左右子树都不能构造合法的连通块,因此需要与 3 3 3 连接才行。

  • 继续思考「连哪个」:

那么,节点 A A A 应该与谁相连呢?对于节点 A A A 的某个子树 T r e e i Tree_i Treei 来说,存在 2 2 2 种情况:

  • 能整除:那么子树 T r e e i Tree_i Treei 不需要和节点 A A A 相连;
  • 不能整除:那么子树 T r e e i Tree_i Treei 的剩余值就必须与节点 A A A 相连,有可能凑出 K K K 的整除。

当节点 A A A 与所有子树的剩余值组合后,再加上当前节点的价值,如果能够构造出 K K K 的整数倍时,说明找到一个新的连通块,并且不需要和上一级节点组合。否则,则进入不能整除的条件,继续和上一级节点组合。

题解(DFS)

  • 定义 DFS 函数并返回两个数值:<子树构造的连通分量, 剩余值>;
  • 任意选择一个节点为根节点走一遍 DFS,最终返回 d f s ( 0 , − 1 ) [ 0 ] dfs(0,-1)[0] dfs(0,1)[0]
class Solution {fun maxKDivisibleComponents(n: Int, edges: Array<IntArray>, values: IntArray, k: Int): Int {// 建图val graph = Array(n) { LinkedList<Int>() }for ((u, v) in edges) {graph[u].add(v)graph[v].add(u)}// DFS <cnt, left>fun dfs(i: Int, pre: Int): IntArray {var ret = intArrayOf(0, values[i])for (to in graph[i]) {if (to == pre) continueval (childCnt, childLeft) = dfs(to, i)ret[0] += childCntret[1] += childLeft}if (ret[1] % k == 0) {ret[0] += 1ret[1] = 0}return ret}return dfs(0, -1)[0]}
}
class Solution:def maxKDivisibleComponents(self, n, edges, values, k):# 建图graph = defaultdict(list)for u, v in edges:graph[u].append(v)graph[v].append(u)# DFS <cnt, left>def dfs(i, pre):ret = [0, values[i]]for to in graph[i]:if to == pre: continuechildCnt, childLeft = dfs(to, i)ret[0] += childCntret[1] += childLeftif ret[1] % k == 0:ret[0] += 1ret[1] = 0return retreturn dfs(0, -1)[0]
class Solution {
public:int maxKDivisibleComponents(int n, vector<vector<int>>& edges, vector<int>& values, int k) {// 建图vector<list<int>> graph(n);for (auto& edge : edges) {int u = edge[0];int v = edge[1];graph[u].push_back(v);graph[v].push_back(u);}// DFS <cnt, left>function<vector<int>(int, int)> dfs = [&](int i, int pre) -> vector<int> {vector<int> ret(2, 0);ret[1] = values[i];for (int to : graph[i]) {if (to == pre) continue;vector<int> child = dfs(to, i);ret[0] += child[0];ret[1] += child[1];}if (ret[1] % k == 0) {ret[0] += 1;ret[1] = 0;}return ret;};return dfs(0, -1)[0];}
};
function maxKDivisibleComponents(n: number, edges: number[][], values: number[], k: number): number {// 建图let graph = Array(n).fill(0).map(() => []);for (const [u, v] of edges) {graph[u].push(v);graph[v].push(u);}// DFS <cnt, left>let dfs = (i: number, pre: number): number[] => {let ret = [0, values[i]];for (let to of graph[i]) {if (to === pre) continue;let [childCnt, childLeft] = dfs(to, i);ret[0] += childCnt;ret[1] += childLeft;}if (ret[1] % k === 0) {ret[0] += 1;ret[1] = 0;}return ret;};return dfs(0, -1)[0];  
};
class Solution {int maxKDivisibleComponents(int n, List<List<int>> edges, List<int> values, int k) {// 建图List<List<int>> graph = List.generate(n, (_) => []);for (final edge in edges) {int u = edge[0];int v = edge[1];graph[u].add(v);graph[v].add(u);}// DFS <cnt, left>List<int> dfs(int i, int pre) {List<int> ret = [0, values[i]];for (int to in graph[i]) {if (to == pre) continue;List<int> child = dfs(to, i);ret[0] += child[0];ret[1] += child[1];}if (ret[1] % k == 0) {ret[0] += 1;ret[1] = 0;}return ret;}return dfs(0, -1)[0];}
}

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n) 每个节点访问 1 1 1 次;
  • 空间复杂度: O ( n ) O(n) O(n) 图空间。

推荐阅读

LeetCode 上分之旅系列往期回顾:

  • LeetCode 单周赛第 364 场 · 前后缀分解结合单调栈的贡献问题
  • LeetCode 单周赛第 363 场 · 经典二分答案与质因数分解
  • LeetCode 双周赛第 113 场 · 精妙的 O(lgn) 扫描算法与树上 DP 问题
  • LeetCode 双周赛第 112 场 · 计算机科学本质上是数学吗?

⭐️ 永远相信美好的事情即将发生,欢迎加入小彭的 Android 交流社群~

相关文章:

LeetCode 周赛上分之旅 #48 一道简单的树上动态规划问题

⭐️ 本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 和 BaguTree Pro 知识星球提问。 学习数据结构与算法的关键在于掌握问题背后的算法思维框架&#xff0c;你的思考越抽象&#xff0c;它能覆盖的问题域就越广&#xff0c;理解难度…...

mysql报错:Column Count Doesn‘t Match Value Count at Row 1

mysql中执行insert、update、delete报错&#xff1a;Column Count Doesnt Match Value Count at Row 1 的解决方案 通常情况&#xff1a;字段不匹配 如&#xff1a;student有id, name, age字段 -- 错误写法 INSERT INTO student VALUES(5,horse)-- 正确写法 INSERT INTO stu…...

安卓 kuaishou 设备did和egid 学习分析

did和egid注册 接口 https://gdfp.ksapisrv.com/rest/infra/gdfp/report/kuaishou/android did 是本地生成的16进制 或者 获取的 android_id public static final Random f16237a new Random(System.currentTimeMillis()); public static long m19668a() { return f1623…...

基于Vue+ELement实现增删改查案例与表单验证(附源码)

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《ELement》。&#x1f3af;&#x1f3af; &#x1…...

webpack:使用externals配置来排除打包后的某个依赖插件IgnorePlugin的使用

背景 假设&#xff0c;我们写了一个库并使用 webpack 打包输出 bundle&#xff0c;但是这个库依赖一个第三方包&#xff0c;比如依赖 lodash&#xff0c;这时候我们不想把这个库打包进 bundle 里因为体积会变大&#xff0c;而且我们的主项目里已经安装了这个 lodash&#xff0…...

2023年中国工业脱水机行业供需分析:随着自动化和智能化技术的快速发展,销量同比增长4.9%[图]

工业脱水机行业是指专门从湿润的固体物料中去除水分的设备制造和相关服务。它广泛应用于食品加工、化工、制药、纺织、环保等行业&#xff0c;用于去除物料中的水分&#xff0c;提高产品质量和降低能耗。 工业脱水机行业分类 资料来源&#xff1a;共研产业咨询&#xff08;共研…...

[论文笔记]MacBERT

引言 今天带来MacBERT的阅读笔记。论文题目是 重新审视中文自然语言处理的预训练模型。 本篇主要是探讨中文预训练语言模型在非英文语言中的有效性,然后提出了一种简单而有效的模型,称为MacBERT,它在多个方面改进了RoBERTa,特别是采用纠错型掩码语言模型(MLM as correcti…...

AI发展目前最大挑战是什么?

影响AI成本的因素包括多个方面&#xff1a; 首先&#xff0c;AI技术的复杂性是其成本高昂的一个重要原因。AI技术需要进行大量数据处理、模型训练和优化&#xff0c;这需要耗费大量的计算资源和时间。同时&#xff0c;AI技术需要高水平的专业人才进行设计、开发和维护&#xf…...

自然语言处理NLP:LTP、SnowNLP、HanLP 常用NLP工具和库对比

文章目录 常见NLP任务常见NLP工具英文NLP工具中文NLP工具 常见NLP任务 Word Segmentation 分词 – Tokenization Stem extraction 词干提取 - Stemming Lexical reduction 词形还原 – Lemmatization Part of Speech Tagging 词性标注 – Parts of Speech Named entity rec…...

百度交易中台之内容分润结算系统架构浅析

作者 | 交易中台团队 导读 随着公司内容生态的蓬勃发展&#xff0c;内容产出方和流量提供方最关注的“收益结算”的工作&#xff0c;也就成为重中之重。本文基于内容分润结算业务为入口&#xff0c;介绍了实现过程中的重难点&#xff0c;比如千万级和百万级数据量下的技术选型和…...

【索引】常见的索引、B+树结构、什么时候需要使用索引、优化索引方法、索引主要的数据结构、聚簇索引、二级索引、创建合适的索引等重点知识汇总

目录 索引的分类 什么时候需要 / 不需要创建索引&#xff1f; 有什么优化索引的方法 MySQL索引主要使用的两种数据结构是什么 为什么 MySQL 采用 B 树作为索引 聚簇索引和二级索引 根据给定的表&#xff0c;如何创建索引比较好 索引的分类 普通索引&#xff1a;最基本的…...

Egg 封装接口返回信息

中间件封装 代码 const msgArr {"200":成功,"401":token失效 } module.exports (option, app) > {return async function(ctx, next) {try{//成功是返回的信息ctx.emit(code,data,msg)>{console.log(1111,code,data,msg)ctx.body {code,data:dat…...

Android AMS——创建APP进程(五)

接上一篇,在 ActivityTaskSupervisor 中会判断进程是否存在,如果进程不存在,则会创建进程,执行 startProcessAsync() 方法。如果进程存在,则执行 realStartActivityLocked() 方法。在APP 的启动时,进程是不存在的。所以我们先来分析一下进程不存在的情况。 一、创建进程…...

凉鞋的 Unity 笔记 102. 场景层次 与 GameObject 的增删改查

102. 场景层次 与 GameObject 的增删改查 在上一篇&#xff0c;我们完成了 Unity 引擎的 Hello world 输出&#xff0c;并且完成了第一个基本循环&#xff1a; 通过这次基本循环的完成&#xff0c;我们获得了一点点的 Unity 使用经验&#xff0c;这非常重要。 有实践经验后再…...

信息安全:网络安全审计技术原理与应用.

信息安全&#xff1a;网络安全审计技术原理与应用. 网络安全审计是指对网络信息系统的安全相关活动信息进行获取、记录、存储、分析和利用的工作。网络安全审计的作用在于建立“事后“安全保障措施&#xff0c;保存网络安全事件及行为信息&#xff0c;为网络安全事件分析提供线…...

嵌入式Linux应用开发-第十三章APP怎么读取按键值

嵌入式Linux应用开发-第十三章读取按键及按键驱动程序 第十三章 APP怎么读取按键值13.1 妈妈怎么知道孩子醒了13.2 APP读取按键的4种方法13.2.1 查询方式13.2.2 休眠-唤醒方式13.2.3 poll方式13.2.4 异步通知方式13.2.4.1 异步通知的原理&#xff1a;发信号13.2.4.2 应用程序之…...

Web 中间件怎么玩?

本次主要是聊聊关于 web 中间件&#xff0c; 分为如下四个方面 什么是 web 框架中间件 为什么要使用 web 中间件 如何使用及其原理 哪些场景需要使用中间件 开门见山 web 中间件是啥 Web 框架中的中间件主要指的是在 web 请求到具体路由之前或者之后&#xff0c;会经过一个或…...

HMTL知识点系列(4)

目录 1. 在你过去的项目中&#xff0c;你如何解决HTML的布局和样式问题&#xff1f;2. 你能否解释一下HTML的“文档对象模型”&#xff08;DOM&#xff09;是什么&#xff0c;以及它的重要性&#xff1f;3. 你有没有经验处理网页的兼容性问题&#xff0c;特别是在不同浏览器之间…...

CFS内网穿透靶场实战

一、简介 不久前做过的靶场。 通过复现CFS三层穿透靶场&#xff0c;让我对漏洞的利用&#xff0c;各种工具的使用以及横向穿透技术有了更深的理解。 一开始nmap探测ip端口,直接用thinkphpv5版本漏洞工具反弹shell&#xff0c;接着利用蚁剑对服务器直接进行控制&#xff0c;留下…...

【RabbitMQ实战】07 3分钟部署一个RabbitMQ集群

一、集群的安装部署 我们还是利用docker来安装RabbitMQ集群。3分钟安装一个集群&#xff0c;开始。 前提条件&#xff0c;docker安装了docker-compose。如果没安装的话&#xff0c;参考这里 docker-compose文件参考bitnami官网&#xff1a;https://github.com/bitnami/contai…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...

MySQL 部分重点知识篇

一、数据库对象 1. 主键 定义 &#xff1a;主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 &#xff1a;确保数据的完整性&#xff0c;便于数据的查询和管理。 示例 &#xff1a;在学生信息表中&#xff0c;学号可以作为主键&#xff…...

android RelativeLayout布局

<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...

Python网页自动化Selenium中文文档

1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API&#xff0c;让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API&#xff0c;你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...

Vue3 PC端 UI组件库我更推荐Naive UI

一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用&#xff0c;前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率&#xff0c;还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库&#xff08;Naive UI、Element …...