力扣每日打卡 3396. 使数组元素互不相同所需的最少操作次数(简单)
力扣 3396. 使数组元素互不相同所需的最少操作次数 简单
- 前言
- 一、题目内容
- 二、解题方法
- 1. 暴力解法
- 2. 哈希函数
- 3. 官方题解
- 3.1 方法一:模拟
- 3.2 方法二:倒序遍历
前言
这是刷算法题的第九天,用到的语言是JS
题目:力扣 3396. 使数组元素互不相同所需的最少操作次数(简单)
一、题目内容
给你一个整数数组 nums,你需要确保数组中的元素 互不相同 。为此,你可以执行以下操作任意次:
从数组的开头移除 3 个元素。如果数组中元素少于 3 个,则移除所有剩余元素。
注意:空数组也视作为数组元素互不相同。返回使数组元素互不相同所需的 最少操作次数 。
示例 1:
输入: nums = [1,2,3,4,2,3,3,5,7]
输出: 2
解释:
第一次操作:移除前 3 个元素,数组变为 [4, 2, 3, 3, 5, 7]。
第二次操作:再次移除前 3 个元素,数组变为 [3, 5, 7],此时数组中的元素互不相同。
因此,答案是 2。
示例 2:
输入: nums = [4,5,6,4,4]
输出: 2
解释:
第一次操作:移除前 3 个元素,数组变为 [4, 4]。
第二次操作:移除所有剩余元素,数组变为空。
因此,答案是 2。
示例 3:
输入: nums = [6,7,8,9]
输出: 0
解释:
数组中的元素已经互不相同,因此不需要进行任何操作,答案是 0。
提示:
1 <= nums.length <= 100
1 <= nums[i] <= 100
二、解题方法
1. 暴力解法
直接双重for循环
!!!需要注意的一点:在每次进行删除操作后,需要将 i 重新赋值为 -1,这是因为删除后,后面的元素会前置,而 i 却是继续往后检索,从而可能导致错过某个元素的检索!!!
代码如下(实例):
/**
* @param {number[]} nums
* @return {number}
*/
var minimumOperations = function(nums) {
// 不知道为什么我总是想到哈希函数
// 先想想有什么暴力解法,然后再优化
// 暴力解法:for循环,一旦遇到相等的元素,马上执行移除前三个元素操作(如果少于三个,则移除所有)
// 每执行一次删除操作,count + 1
let count = 0
for(let i = 0; i < nums.length - 1; i++) {
for(let j = i + 1; j < nums.length; j++) {
if(nums[i] === nums[j]) {
if(nums.length < 3) {
nums.splice(0) // 从索引0开始,删到最后一个
count++
i = -1 // 重新开始遍历 !!!!!!
} else {
nums.splice(0, 3) // 从索引0开始,移除前三个元素
count++
i = -1 // 重新开始遍历 !!!!!!
}
}
}
}
return count
}
2. 哈希函数
使用哈希函数来解决,若哈希表中含有该元素,则进行相应的 删除 操作,并且记录count+1
注意:循环结束的 条件
代码如下(示例):
/**
* @param {number[]} nums
* @return {number}
*/
var minimumOperations = function (nums) {
// 不知道为什么我总是想到哈希函数
// 先想想有什么暴力解法,然后再优化
// 哈希函数 遍历数组
// 判断当前元素是否存在于hash表中,如果存在,则进行删除操作,
// 此处遇到的坑,while循环的语句结束条件的错误!!!
// 具体表现为:如果当前数组是互不相同的数组了,但是数组的长度又大于0,则while循环不会结束,所以要手动结束
// 手动结束的条件一直没有想到,不知道怎么写
// 但是肯定是nums空了或者其中的元素互不相同就结束了
// 所以干脆在while循环时进行判断,在 nums空了或者其中的元素互不相同 时就不再进行while循环
let count = 0
while (nums.length > 0 && nums.length !== new Set(nums).size) {
const seen = new Set() // 注意,此处集合要在循环体里面定义
for (let i = 0; i < nums.length; i++) {
// 如果当前元素存在于哈希表(集合)中,则进行nums的删除操作,并且count的记录 +1,
if (seen.has(nums[i])) {
count++
const num = nums.length >= 3 ? 3 : nums.length
nums.splice(0, num)
// nums.splice(0, Math.min(3, nums.length)) 二选一
break
}
seen.add(nums[i])
}
// 此处是结束while循环的关键!!! 不要了,这里有误
// if (seen.size === nums.length) break
// 在这行代码中,while 循环的结束条件是通过 if (seen.size === nums.length) break 来判断的,但这可能会导致逻辑不够清晰。可以改进为直接在 while 循环的条件中加入更明确的判断条件。
// 改进后的 while 循环条件如下:
// while (nums.length > 0 && new Set(nums).size !== nums.length) {}
// 这样,循环会在以下两种情况下结束:
// 数组为空(nums.length === 0)
// 数组中的元素已经互不相同(new Set(nums).size === nums.length) 因为集合中的元素是互不相同的
// Set { 1, 2, 3 } nums[1, 1, 1, 2, 2, 3] nums转换成集合就是Set(3) { 1, 2, 3 }
}
return count
}
3. 官方题解
3.1 方法一:模拟
思路与算法
题目要求执行操作使得数组中的剩余元素是否互不相同,最直接的方法即每次从数组开头跳过 3 个元素,并检测数组中剩余元素是否存在重复元素,我们可以用一个哈希表来检测数组是否存在重复元素即可。
代码如下(示例):
var minimumOperations = function(nums) {
let ans = 0;
for (let i = 0; i < nums.length; i += 3, ans++) {
if (checkUnique(nums, i)) {
return ans;
}
}
return ans;
};
const checkUnique = (nums, start) => {
let seen = new Set();
for (let i = start; i < nums.length; i++) {
if (seen.has(nums[i])) {
return false;
}
seen.add(nums[i]);
}
return true;
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/minimum-number-of-operations-to-make-elements-in-array-distinct/solutions/3634685/shi-shu-zu-yuan-su-hu-bu-xiang-tong-suo-cay1s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
-
复杂度分析:
- 时间复杂度:O(n 2),n 表示给定数组 nums 的长度。每次检测剩余的数组中是否存在重复元素,需要时间最多为 O(n),一共最多需要检测 n 次,因此总的时间为 O(n 2)。
- 空间复杂度:O(n),n 表示给定数组 nums 的长度。每次检测数组是否含有重复元素时,需要用哈希表记录已经出现的元素,最多存在 n 个元素需要记录,因此需要的空间为 O(n)。
链接:力扣本题官方题解
来源:力扣(LeetCode)
3.2 方法二:倒序遍历
思路与算法
假设重复元素 x 在数组索引 i,j 处出现,若此时满足 i < j,则至少需要移除索引 i 之前所有的元素,则此时问题转换为求数组满足所有元素互不相同的最长后缀。由于每次需要移除 3 个元素,此时移除索引 i 之前的所有元素 nums[0⋯i],至少需要 ⌈(i+1) / 3⌉ = ⌊i / 3⌋ + 1 次移除操作。
假设数组长度为 n,我们尝试倒序遍历数组,同时用 seen 记录已经出现的元素,当遍历到第一个重复元素 nums[i] 时,即该元素已经在当前的后缀中存在,此时返回最少操作次数 ⌊i / 3⌋ + 1 ,如果数组中不存在重复元素,则返回 0。
代码如下(示例):
var minimumOperations = function(nums) {
const seen = new Array(128).fill(false);
for (let i = nums.length - 1; i >= 0; i--) {
if (seen[nums[i]]) {
return Math.floor(i / 3) + 1;
}
seen[nums[i]] = true;
}
return 0;
};
作者:力扣官方题解
链接:https://leetcode.cn/problems/minimum-number-of-operations-to-make-elements-in-array-distinct/solutions/3634685/shi-shu-zu-yuan-su-hu-bu-xiang-tong-suo-cay1s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
-
复杂度分析:
- 时间复杂度:O(n),n 表示给定数组 nums 的长度。只需遍历一遍数组即可,需要的时间为 O(n)。
- 空间复杂度:O(n),n 表示给定数组 nums 的长度。需要使用哈希表保存已经遍历过的数据,最多需要保存 n 个元素,需要的空间为 O(n)。
链接:力扣本题官方题解
来源:力扣(LeetCode)