算法优化:LeetCode第122场双周赛解题策略与技巧

接下来会以刷常规题为主 ,周赛的难题想要独立做出来还是有一定难度的,需要消耗大量时间

比赛地址 

3011. 判断一个数组是否可以变为有序

public class Solution {public int minimumCost(int[] nums) {if (nums.length < 3) {// 数组长度小于3时,无法分割成3个子数组return -1;}int minCost = Integer.MAX_VALUE;int n = nums.length;// 第一个分割点至少在索引1,第二个分割点至少在索引2for (int i = 1; i < n - 1; i++) {for (int j = i + 1; j < n; j++) {int cost = nums[0] + nums[i] + nums[j];minCost = Math.min(minCost, cost);}}return minCost;}
}

100164. 通过操作使数组长度最小

冒泡排序

class Solution {public boolean canSortArray(int[] nums) {int n = nums.length;for (int i = 0; i < n - 1; i++) {for (int j = 0; j < n - i - 1; j++) {if (Integer.bitCount(nums[j]) == Integer.bitCount(nums[j + 1]) && nums[j]>nums[j + 1]) {// 如果前一个元素的1的数量大于后一个元素的1的数量,交换它们int temp = nums[j];nums[j] = nums[j + 1];nums[j + 1] = temp;}}}// 遍历完后,检查数组是否有序for (int i = 0; i < n - 1; i++) {if (nums[i] > nums[i + 1]) {return false;}}return true;}
}

100181. 将数组分成最小总代价的子数组 I

当 x<y 时,x  mod  y=x 因此如果选择数组中的两个不相等的元素,则可以删除较大元素,保留较小元素。

用 minNum表示数组 nums 中的最小元素

用 minCount 表示数组 nums 中的 minNum 的出现次数

分别考虑 minCount=1 和 minCount>1 的情况。

  • 如果 minCount=1

则可以每次选择 minNum  和另一个元素,由于 minNum 一定小于另一个元素,因此总是可以删除另一个元素,保留 minNum,直到数组 nums  中只有一个元素 minNum,数组 nums的最小长度是 1。

  • 如果 minCount>1 
  1. 如果数组 nums 中存在一个元素 num 满足 num mod minNum≠0 ,记 newNum= (num  mod  minNu)
  2. 则必有 0<newNum<minNum 可以在一次操作中选择 num 和 minNum ,删除这两个元素并添加元素newNum。
  3. 由于 newNum < minNum ,因此 newNum 成为数组 nums 中的新的最小元素且最小元素唯一,之后可以每次选择 newNum 和另一个元素,其效果是删除另一个元素,保留 newNum ,直到数组 nums 中只有一个元素 newNum ,数组 nums 的最小长度是 1
  4. 如果数组 nums 中不存在元素 num 满足 num  mod  minNum ≠ 0 ,则无法通过操作得到小于 minNum 的元素,因此在将所有大于 minNu 的元素删除之后,剩余 minCount 个元素 minNum 。由于每次可以选择 2 个元素 minNum 执行操作得到元素 0 无法继续操作,因此 minCount 个元素 minNum 的最多操作次数可以根据count_min的奇偶性判断
class Solution:def minimumArrayLength(self, nums: List[int]) -> int:min_val = min(nums)count_min = nums.count(min_val)for num in nums:if num % min_val != 0:return 1  # 产生了新的更小值# 没有产生新的最小值,计算最小值的数量return (count_min ) // 2 +1 if count_min % 2 != 0 else count_min // 2

100178. 将数组分成最小总代价的子数组 II

一、直接用滑动窗口求解

这种方法会超时

class Solution:def minimumCost(self, nums: List[int], k: int, dist: int) -> int:first = nums[0]  # 初始元素的代价window_size = dist + 1  # 窗口大小minimumCost = float('inf')  # 初始化最小代价为无穷大# 遍历数组,寻找除第一个和最后一个元素之外的最小的 k-1 个元素for start in range(1, len(nums) - window_size + 1):window = nums[start:start + window_size]sorted_window = sorted(window)# 获取除第一个的 k-1 个最小元素的和window_cost = sum(sorted_window[:k-1])# 更新最小代价minimumCost = min(minimumCost, window_cost)# 最终的最小总代价是第一个元素的代价加上最小窗口代价return first + minimumCost 

二、引入堆的代码实现

效率和之前的方法相差无几

class Solution:def minimumCost(self, nums: List[int], k: int, dist: int) -> int:first = nums[0]n = len(nums)minimumCost = float('inf')for start in range(1, n - dist):# 维护一个大小为 dist + 1 的最小堆min_heap = nums[start:start + dist + 1]heapq.heapify(min_heap)window_cost = 0# 弹出最小的 k-1 个元素并计算它们的和for _ in range(k-1):if min_heap:window_cost += heapq.heappop(min_heap)minimumCost = min(minimumCost, window_cost)return first + minimumCost

三、大小顶堆、延迟删除、滑动窗口

这道题目的思路是利用滑动窗口结合两个堆(优先队列)来找出序列中指定数量(`k-1`)的最小数的和,它们是从序列的某个区间(该区间长度由`dist`决定)中选择出来的。这个序列中的第一个数 (`nums[0]`) 是固定的,所以总是被包含在结果中。

下面是详细的解题步骤:

  1. 初始化两个堆:一个小顶堆 small 来保存当前窗口中的最小的 k-2 个数,以及一个大顶堆 big 来保存窗口内剩余的数。

  2. 使用 HashMap 进行延迟删除:为了实现有效地从堆中删除特定的非堆顶元素,创建两个 HashMap (smallMark 和 bigMark) 来标记堆中元素是否已经被 "删除"。该删除实际上是延迟执行的,即直到这个元素出现在堆顶时才真正被排除。

  3. 填充初始窗口:从 nums 数组的第二个元素开始,将 dist+1 长度内的元素放入 big 堆。

  4. 从 big 中取出 k-2 个最小元素:这 k-2 个元素是将要加入 small 的,记录这 k-2 个数的和作为窗口的当前总和。

  5. 滑动窗口:在数组中滑动窗口,并动态维护这两个堆以保持正确的最小 k-2 个数的总和。

  6. 调整堆:当窗口滑动导致元素移出窗口时,更新 small 堆以保持其有效性,并进行相应的调整。如果移出的元素当前在 small 中,则它需要被标记为已删除;如果它在 big 中,则直接标记为已删除。

  7. 处理新进入窗口的元素:窗口滑动时,可能会有新的元素进入。这些新元素需要加入到 big 堆中。从 big 中取出的最小元素会放入 small 堆,并更新当前窗口总和(sum)。

  8. 求解最终结果:在滑动窗口过程中,每次窗口更新后,计算此时的窗口总和加上 nums[0](固定加入)。所有窗口中总和的最小值即为所求问题的答案。

class Solution {// small是小顶堆 维护前k-2小的数// big是大顶堆 维护窗口内剩下的数PriorityQueue<Integer> small, big;// 标记当前元素是否已经被删除以及被删除的个数HashMap<Integer, Integer> smallMark, bigMark;// samll和big当前未被删除的元素个数int smallSiz, bigSiz;long sum;public long minimumCost(int[] nums, int k, int dist) {// k个 除掉第一个 还要选k-1个// 枚举第2个 nums[i] nums[i+1]... nums[i+dist] 里选k-2个最小的数// nums[i+1] nums[i+k-2]small = new PriorityQueue<>(Collections.reverseOrder());smallSiz = 0;smallMark = new HashMap<>();big = new PriorityQueue<>();bigSiz = 0;bigMark = new HashMap<>();// 当前小顶堆的和 也就是前k-2小的和sum = 0;int n = nums.length;// 把nums[1+1]...nums[1+dist]里的数加入到big里for (int i = 2; i <= Math.min(n-1, dist+1); i++) {big.add(nums[i]);bigSiz++;}// 取出前k-2小的数放入smallfor (int i = 0; i < k-2; i++ ) {int tmp = big.poll();bigSiz--;sum += tmp;small.add(tmp);smallSiz++;}long res = nums[0] + nums[1] + sum;// 枚举第二个数的位置// 枚举的位置从i-1变成i时 nums[i]离开了窗口 nums[i+dist]进入了窗口for (int i = 2; i + k-2 < n; i++) {// 移除nums[i]// 因为要访问small.peek() 为了确保small.peek()是未被删除的元素 需要先更新smallupdateSmallPeek();// nums[i]在前k-2小里if (smallSiz > 0 && small.peek() >= nums[i]) {// 因为nums[i] 是可能小于small.peek()的 我们没法直接删除nums[i] 所以要标记一下smallMark.merge(nums[i], 1, Integer::sum);// 从small里删除nums[i]smallSiz--;sum -= nums[i];} else {// nums[i]不在前k-2小里 bigMark.merge(nums[i], 1, Integer::sum);bigSiz--;// 这里是为了使得small的数量变成k-3个 也就是还差一个才够k-2个// 是为了方便后面的操作// 从small里选一个放到big里int tmp = small.poll();smallSiz--;sum -= tmp;big.add(tmp);bigSiz++;}// 先放到big里 然后从big里面拿一个放到small就刚好k-2个if (i+dist < n) {big.add(nums[i+dist]);bigSiz++;}// 要从big里拿一个 访问big.peek()之前要先更新bigupdateBigPeek();int tmp = big.poll();bigSiz--;sum += tmp;small.add(tmp);smallSiz++;res = Math.min(res, nums[i] + nums[0] + sum);}return res;}// 每次访问small.peek()之前都要先更新smallpublic void updateSmallPeek() {// 如果small.peek()已经被删除了 那么就把它从small里移除 直到small.peek()是未被删除的元素while (smallSiz > 0 && smallMark.getOrDefault(small.peek(), 0) > 0) {int tmp = small.poll();smallMark.merge(tmp, -1, Integer::sum);}}public void updateBigPeek() {while (bigSiz > 0 && bigMark.getOrDefault(big.peek(), 0) > 0) {int tmp = big.poll();bigMark.merge(tmp, -1, Integer::sum);}}
}

这个方法高效地使用了堆结构来保持每次窗口移动后,都能快速地选择出当前窗口中的k-2个最小数,而HashMap的标记删除机制则可以绕过优先队列不支持直接删除的限制。通过这个算法,你可以在移动窗口的过程中,不断更新当前窗口的最小值和,最终得到包含`nums[0]`在内的最小成本和。

思考1:为什么要用大顶堆只用小顶堆会怎么样?

因为小顶堆只能让您迅速访问堆中的最小值,而不是最大值。因此,如果窗口中有一个更小的数字需要加入到已满的小顶堆中(这时候我们需要替换掉小顶堆中最大的数字),您需要一种方式来找到小顶堆中的最大值,而大顶堆允许我们做到这一点。 

思考2:bigMark.merge(tmp, -1, Integer::sum)这个是干什么

在Java中的 PriorityQueue 并没有提供直接删除特定元素的操作,而是只提供了删除堆顶元素的操作。为了解决这个问题,bigMark 的用途是实现“延迟删除”,这个技巧通常在优先队列中删除非顶部元素时使用。

  • bigMark 是一个 HashMap,它的键是元素值,值是该元素被标记删除的次数。
  • 当我们要从优先队列 big 中删除一个元素时,我们不能直接删除它,因为它可能不在堆顶。
  • 所以我们在 bigMark 中对这个元素的删除次数加一。这个标记表示元素已经被逻辑上删除,尽管它仍在优先队列中。
  • merge 方法是一个合并函数,它会检查 HashMap 中是否存在键 tmp
    • 如果存在,它会使用提供的合并函数 Integer::sum 将当前值与给定值相加。
    • 如果没有找到键 tmp,它会插入键值对 tmp -> -1
  • 在这个场景中,merge 方法用 -1 更新 tmp 的删除次数。每次 tmp 出现在堆顶时,这个标记都会被检查。如果标记表示该元素被删除(即删除计数大于零),这个元素将会从堆中弹出,同时更新它在 bigMark 中的标记。
// 假设堆中有一个元素值为 5,现在我们要删除它:
int tmp = 5;
bigMark.merge(tmp, 1, Integer::sum); // 标记 tmp 为已删除// 当我们后续从堆中得到堆顶元素时:
updateBigPeek(); // 在访问堆顶前更新堆// updateBigPeek 的实现会检查堆顶元素是否被标记为已删除,如果是,就将其从堆中移除,
// 并在 bigMark 中更新其计数:
public void updateBigPeek() {while (bigSiz > 0 && bigMark.getOrDefault(big.peek(), 0) > 0) {int tmp = big.poll(); // 弹出堆顶元素bigMark.merge(tmp, -1, Integer::sum); // 更新 bigMark,减少删除计数}
}

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

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

相关文章

UG制图-创建图纸的多种方法

1、2D&#xff1a;创建独立2D图纸&#xff0c;不引用任何3D模型 在UG软件中选择新建&#xff0c;或者快捷键ctrl N&#xff0c;进入新建命令&#xff0c;然后点击图纸&#xff0c;在关系中选择独立的部件&#xff0c;就创建了一个独立的图纸&#xff0c;我们可以在装配中添加…

项目管理该考哪个证书❓NPDP还是软考❓

有小伙伴在纠结是要考NPDP认证呢还是考软考呢❓ 今天小编要给大家好好说说NPDP认证❗️ &#x1f4a1;NPDP全称New Product Development Professional&#xff0c;也就是产品经理国际资格认证。 &#x1f525;NPDP是国际公认的为一的新产品开发专业认证&#xff0c;是集理论、方…

「环境配置」使用Windows自带工具清理C盘空间

​ Windows电脑操作系统一般是安装在磁盘驱动器的C盘中&#xff0c;一旦运行&#xff0c;便会产生许多垃圾文件&#xff0c;C盘空间在一定程度上都会越来越小。伴随着电脑工作的时间越久&#xff0c;C盘常常会提示显示其内存已不足。本文记录笔者清理机器的步骤。 一、使用Win…

在Qt中通过控制按钮实现登录界面密码与明码的转换

创建控件&#xff1a; 首先&#xff0c;在Qt设计师界面界面上创建QLineEdit类文本框&#xff0c;用于输入密码&#xff0c;并且实现密码与明码相互转化。 设置初始状态&#xff1a; 默认情况下&#xff0c;输入密码的文本框应该是可见的并允许用户输入。 添加切换按钮&…

【Oracle】收集Oracle数据库内存相关的信息

文章目录 【Oracle】收集Oracle数据库内存相关的信息收集Oracle数据库内存命令例各命令的解释输出结果例参考 【声明】文章仅供学习交流&#xff0c;观点代表个人&#xff0c;与任何公司无关。 编辑|SQL和数据库技术(ID:SQLplusDB) 【Oracle】收集Oracle数据库内存相关的信息 …

【Elasticsearch】索引恢复(recovery)流程梳理之副本分片数据恢复

replica shard重启具体流程 replica shard node &#xff08;generic threadpool&#xff09; 也是因为应用新的集群状态触发recovery&#xff0c;进入index阶段进入translog 阶段。先尝试重放本地的translog到global checkpoint向primary shard发起start recovery的请求&…

algotithm -- 排序算法

排序算法总结表&#xff1a; 1. In-place 和 Out-place 含义 参考链接 in-place 占用常数内存&#xff0c;不占用额外内存 假如问题规模是n&#xff0c;在解决问题过程中&#xff0c;只开辟了常数量的空间&#xff0c;与n无关&#xff0c;这是原址操作&#xff0c;就是In-…

HarmonyOS开源软件Notice收集策略说明

开源软件Notice是与项目开源相关的文件&#xff0c;收集这些文件的目的是为了符合开源的规范。 收集目标 只收集打包到镜像里面的模块对应的License&#xff1b;不打包的都不收集&#xff0c;比如构建过程使用的工具&#xff08;如clang、python、ninja等&#xff09;都是不收…

【C语言基础考研向】08判断语句与循环语句

1.关系表达式与逻辑表达式 算术运算符的优先级高于关系运算符、关系运算符的优先级高于逻辑与和逻辑或运算符、相同优先级的运算符从左至右进行结合等&#xff0c;那么表达式5>3&&8<4-!0的最终值是多少?其计算过程如下图所示。 2.if-else语句 引入:在你打开衣…

[小程序]使用代码渲染页面

一、条件渲染 1.单个控制 使用wx:if"{{条件}}"来判断是否需要渲染这段代码&#xff0c;同时可以结合wx:elif和wx:else来判断 <view wx:if"{{type0}}">0</view> <view wx:elif"{{type1}}">1</view> <view wx:else>…

MySQL UNION 操作符

昨天介绍了 MySQL 数据库使用 LIKE 子句来进行筛选查询&#xff0c;今天主要讲解下 MySQL UNION 操作符。 MySQL UNION 操作符用于连接两个以上的 SELECT 语句的结果组合到一个结果集合&#xff0c;并去除重复的行。UNION 操作符必须由两个或多个 SELECT 语句组成&#xff0c;每…

Angular系列教程之父子组件通信详解

文章目录 前言组件通信方法1. 输入属性&#xff08;Input Properties&#xff09;2. 输出属性&#xff08;Output Properties&#xff09;3. 服务&#xff08;Services&#xff09;4. ViewChild与ContentChild 示例代码说明结论 前言 在Angular应用程序开发中&#xff0c;父子…

将输入框的数据输出成json

刚学&#xff0c;做一下记录 先上效果图&#xff0c;来不及解释了&#xff0c;后面再说 源码&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1pV8hDVwpB1kresHag7gIew 提取码&#xff1a;**** 操作&#xff1a; 进入项目目录下&#xff0c;cmd 清除npm缓存&#xff1…

(二十三)Kubernetes系列之prometheus+grafana安装

1.kube-prometheus安装 1.1 下载 访问地址&#xff1a;https://github.com/prometheus-operator/kube-prometheus/releases 点击Source code (tar.gz)进行下载&#xff0c;并上传到k8s服务器master节点 1.2解压 tar zxvf kube-prometheus-0.11.0.tar.gz 1.3进入kube-prome…

C++函数对象-函数包装器-调用空的 std::function 时抛出的异常(std::bad_function_call)

任何定义了函数调用操作符的对象都是函数对象。C 支持创建、操作新的函数对象&#xff0c;同时也提供了许多内置的函数对象。 函数包装器 std::function 提供存储任意类型函数对象的支持。 用空的 std::function 时抛出的异常 std::bad_function_call class bad_function_cal…

“gradle project sync failed”

很久没打开AndroidStudio了&#xff0c;打开电脑发现这个软件都没了。重新安装后创建项目不成功&#xff0c;就提示了这个错误。 错误原因 “gradle project sync failed”&#xff1a;gradle没有配置成功。在安卓项目下找到目标文件&#xff1a;gradle --> wrapper -->…

尝试解决githubclone失败问题

BV1qV4y1m7PB 根据这个视频 似乎是我的linux的github似乎下好了 我没有配置好 比如我的ssh-key 现在根据视频试试 首先需要跳转到ssh的文件夹&#xff1a; cd ~/.ssh 然后生成一个ssh-key&#xff1a; ssh-keygen -t rsa -C "<github资料里的邮箱>" 然后…

IaC基础设施即代码:Terraform 创建 docker 网络与容器资源

目录 一、实验 1.环境 2.Terraform查看版本 3.Linux主机安装Docker 4.Terraform使用本地编译&#xff08;In-house&#xff09;的Providers 5.Docker-CE 开启远程API 6. Linux主机拉取镜像 7.Terraform 创建docker 网络资源 8.Terraform 创建docker 容器资源 一、实验 …

C语言算法赛——蓝桥杯(省赛试题)

一、十四届C/C程序设计C组试题 十四届程序C组试题A#include <stdio.h> int main() {long long sum 0;int n 20230408;int i 0;// 累加从1到n的所有整数for (i 1; i < n; i){sum i;}// 输出结果printf("%lld\n", sum);return 0; }//十四届程序C组试题B…

[机缘参悟-129] :我个人对人生之苦解决之道的思考

目录 前言&#xff1a; 第1层&#xff1a;环境层 1.1 环境的分类 1.2 理解环境的运作的基本原理 1.3 主动选择适合自己的愉快的环境 1.4 主动构建适合自己的愉快的环境 第2层&#xff1a;生理层 2.1 生理健康和情绪之间的关系 2.2 学习人的生物、生理、健康的基本知识…