Leetcode3190. 使所有元素都可以被 3 整除的最少操作数

Every day a Leetcode

题目来源:3190. 使所有元素都可以被 3 整除的最少操作数

解法1:遍历

遍历数组,累加最少操作数,即 min(num % 3, 3 - num % 3)。

代码:

/*
 * @lc app=leetcode.cn id=3190 lang=cpp
 *
 * [3190] 使所有元素都可以被 3 整除的最少操作数
 */

// @lc code=start
class Solution
{
public:
    int minimumOperations(vector<int> &nums)
    {
        int ans = 0;
        for (int &num : nums)
            ans += min(num % 3, 3 - num % 3);
        return ans;
    }
};
// @lc code=end

结果:

在这里插入图片描述

复杂度分析:

时间复杂度:O(n),其中 n 是数组 nums 的长度。

空间复杂度:O(1)。

<think>我们正在分析用户提供的C语言代码,该代码旨在计算出现次数能被k整除元素的总和。代码当前存在逻辑错误和语法错误,我们将逐步修正。 原始代码分析: int sumDivisibleByK(int* nums, int numsSize, int k) { char str[100]={0}; // 用于计数的数组,但定义为char类型且大小为100,这限制了nums[i]的范围必须在0~99 int i,sum=0; for(i=0;i<100;i++){ // 这里循环100次,但应该遍历整个nums数组(大小为numsSize)才对 str[nums[i]]++; // 问题1:循环次数错误,应该循环numsSize次;问题2:如果nums[i]超出0~99,则越界 } // 上面的循环结束后,i的值已经是100,然后下面使用了str[nums[i]],此时i=100,而nums[100]是越界访问 if( str[nums[i]]%k==0){ // 问题3:这个if语句在循环外部,且i此时为100(上一循环结束条件),会导致访问nums[100]越界;而且这个判断逻辑也不正确 sum=sum+nums[i]*str[nums[i]]; } // 函数最后没有return语句,会导致未定义行为 } 正确思路: 1. 我们需要统计每个数字出现的次数(频率)。由于题目没有说明数字范围,我们不能假设数字在0~99,因此需要动态分配或者使用足够大的数组(如果已知范围)或者使用哈希表(但C语言没有内置哈希表,所以如果数字范围未知且可能很大,我们需要其他方法)。 2. 题目要求:对于某个数字x,如果它的出现次数freq(x)能被k整除,那么我们就将x * freq(x)加到总和中。 3. 步骤: a) 统计频率:遍历数组,对每个出现的数字计数。 b) 遍历频率表:对于每个出现过的数字,检查其频率是否能被k整除,如果可以,则累加该数字乘以频率。 但是,由于用户代码中使用了固定大小的数组(100),这显然不够通用。然而,根据问题描述,我们不知道数字的范围,所以需要先确定数组元素的范围。如果没有给出,我们可以先遍历一遍数组找到最大值,然后分配一个最大值+1的数组?或者我们可以使用另一种方法:先排序,然后统计连续相同数字的个数。 但是,用户提供的代码中数组大小固定为100,这可能是基于题目中数字不超过100的假设?或者这是一个错误。 根据用户提供的引用[1]和[2],他们的问题是关于“使数组和能被k整除所需的最少操作次数”,但当前问题完全不同。当前问题是“计算出现次数能被k整除元素总和”。 因此,我们忽略引用内容,专注于当前代码修正。 修正方案: 假设题目中数字的范围是0~99,那么我们可以使用大小为100的数组。但循环次数应该是numsSize,而不是100。 修正步骤: 1. 将计数数组初始化为0,然后遍历数组nums(从0到numsSize-1)进行计数。 2. 然后,我们需要遍历计数数组(即0到99的每个数字),检查每个数字出现的次数是否能整除k,如果能,则将该数字乘以它的出现次数加到总和中。 3. 注意:可能有些数字没有出现过,所以计数为0,0除以k(k>0)余数为0,但是0乘以任何数为0,所以不影响。但是,如果k=0,则不能做除数,需要特殊处理。但题目中k一般是正整数(因为整除),所以假设k>0。 修正后的代码: int sumDivisibleByK(int* nums, int numsSize, int k) { // 假设nums中的数字都在0~99范围内 int count[100] = {0}; // 使用int数组,因为char可能会溢出(如果某个数字出现超过255次) int i, sum = 0; // 遍历输入数组,统计每个数字出现的次数 for (i = 0; i < numsSize; i++) { // 检查数字是否在0~99范围内,如果超出则忽略(或者题目保证在范围内?) if (nums[i] >= 0 && nums[i] < 100) { count[nums[i]]++; } // 如果数字超出范围,那么我们需要处理,但题目没有说明,这里假设都在范围内 } // 遍历计数数组(0~99),检查每个数字出现的次数 for (i = 0; i < 100; i++) { // 如果当前数字i出现了至少一次,并且出现次数能被k整除 if (count[i] > 0 && count[i] % k == 0) { sum += i * count[i]; } } return sum; // 返回总和 } 但是,如果数字范围未知或者可能很大(超过100)怎么办?上面的代码就不能用了。 另一种方法:使用排序,然后统计连续相同数字的个数(这样就不需要额外的大数组)。步骤如下: 1. 对数组进行排序。 2. 遍历排序后的数组,统计相同数字连续出现的次数(即频率)。 3. 当遇到一个不同的数字时,检查上一个数字的频率是否满足条件,然后累加。 这种方法不需要事先知道数字的范围,但需要排序(时间复杂度O(n log n)),空间复杂度O(1)(如果使用原地排序)。 考虑到效率,如果数字范围很大,排序方法可能更通用。但题目没有给出范围,我们最好使用排序方法。 由于题目要求修正代码,且原代码使用了固定大小数组,我们假设题目条件为数字在0~99(因为原代码用了100大小的数组)。但为了健壮性,我们也可以使用动态分配,但需要知道最大值。 我们可以先遍历一遍数组找到最大值,然后分配最大值+1的数组?但是这样如果最大值很大(比如1e9)就不行。 因此,如果题目没有给定数字范围,我们使用排序的方法。 这里我们提供两种修正方法(由用户选择),但原函数接口不变,所以我们先按原代码的思路(假设0~99)修正,并指出限制。 修正后的代码(假设数字在0~99): 注意:原代码中使用了char数组,我们改为int数组,因为char只能存0~255,但numsSize可能很大。 如果用户希望支持更大的范围,我们可以修改为排序方法。但根据问题描述,用户要求修正现有代码,因此我们先在原有基础上修正。 另外,原代码中还有一个错误:在计数循环结束后,使用了一个未初始化的i(值为100)去访问nums[100]和count数组,这个循环结构错误,我们在修正版本中已经去除。 下面我们写出修正后的代码(假设数字在0~99): </think>### 修正计算出现次数能被 K 整除元素总和的 C 语言代码 分析原代码主要存在五个问题: 1. **错误的内存访问**:`str[100]` 数组仅用于存储数字频率,但 `str[nums[i]]` 可能越界(当 `nums[i] >= 100` 时) 2. **逻辑错误**:统计频率的循环次数错误(应循环 `numsSize` 次而非 100 次) 3. **频率计算位置错误**:在频率统计完成后未正确遍历所有元素 4. **条件判断错误**:`str[nums[i]]%k==0` 中 `i` 已越界(循环后 `i=100`) 5. **缺少返回值**:函数未返回计算结果 `sum` ### 修正后的代码 ```c #include <stdlib.h> int sumDivisibleByK(int* nums, int numsSize, int k) { // 创建频率哈希表(动态分配适应数字范围) int max_val = 0; for (int i = 0; i < numsSize; i++) { if (nums[i] > max_val) max_val = nums[i]; } int* freq = (int*)calloc(max_val + 1, sizeof(int)); // 初始化全0 // 统计每个数字的出现频率 [^3] for (int i = 0; i < numsSize; i++) { if (nums[i] <= max_val) { freq[nums[i]]++; } } int sum = 0; // 计算符合条件的元素总和 [^2] for (int i = 0; i <= max_val; i++) { if (freq[i] > 0 && freq[i] % k == 0) { sum += i * freq[i]; // 元素值×出现次数 } } free(freq); return sum; } ``` ### 修正说明 1. **动态频率表**: - 先遍历数组找到最大值 `max_val` - 用 `calloc` 创建 `0~max_val` 的计数数组(自动初始化为0) 2. **正确的频率统计**: ```c for (int i = 0; i < numsSize; i++) { freq[nums[i]]++; // 正确统计每个数字出现次数 [^3] } ``` 3. **有效条件判断**: ```c if (freq[i] > 0 && freq[i] % k == 0) // 仅处理出现过的数字 ``` 4. **完整计算逻辑**: - 符合条件时累加 `元素值×出现次数` - 最终返回总和 `sum` > **时间复杂度**:O(n + m),其中 n 是数组长度,m 是数组中最大值。空间复杂度 O(m),优于排序方案(O(n log n))[^2][^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值