6005.使数组变成交替数组的最少操作数(贪心)

398 阅读2分钟

「这是我参与2022首次更文挑战的第24天,活动详情查看:2022首次更文挑战」。

每日刷题第46天 2021.02.13

6005.使数组变成交替数组的最少操作数

题目

  • 给你一个下标从 0 开始的数组 nums ,该数组由 n 个正整数组成。
  • 如果满足下述条件,则数组 nums 是一个 交替数组 :
nums[i - 2] == nums[i] ,其中 2 <= i <= n - 1 。
nums[i - 1] != nums[i] ,其中 1 <= i <= n - 1
  • 在一步 操作 中,你可以选择下标 i 并将 nums[i] 更改 为 任一 正整数。
  • 返回使数组变成交替数组的 最少操作数。

示例

  • 示例1
输入:nums = [3,1,3,2,4,3]
输出:3
解释:
使数组变成交替数组的方法之一是将该数组转换为 [3,1,3,1,3,1] 。
在这种情况下,操作数为 3 。
可以证明,操作数少于 3 的情况下,无法使数组变成交替数组。
  • 示例2
输入:nums = [1,2,2,2,2]
输出:2
解释:
使数组变成交替数组的方法之一是将该数组转换为 [1,2,1,2,1].
在这种情况下,操作数为 2 。
注意,数组不能转换成 [2,2,2,2,2] 。因为在这种情况下,nums[0] == nums[1],不满足交替数组的条件。

提示

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105

解法

分析情况,进行判断

  • 首先,从手写思考样例可以得到,最终的交替数组形式均为[a,b,a,b,a,b,a] 或 [a,b,a,b,a,b]的形式。因此可以贪心的想,取ab均为数组中最长的两个数,那么需要修改的数值就会最少。此处的a必须是偶数位最多的数,b必须是奇数位最多的数。
  • 使用map集合统计奇数mapOdd和偶数mapEven的数值出现的次数,并降序排序。取排好序后的两个数组的最大值,即:当前偶数位和奇数位的最多的值。判断这个两个值是否相等?
  • 不相等:最小操作数ans = 数组的长度len - (奇数最多的值次数 + 偶数最多的值次数)
  • 相等:
    • 判断偶数最多的值出现的次数 < 奇数最多的值出现的次数。选择更换较小的一方(偶数最多的值出现的次数),取其次长的数值出现的次数 和 奇数最多的值出现的次数。
    • 偶数最多的值出现的次数 > 奇数最多的值出现的次数。选择更换较小的一方(奇数最多出现的次数),取其次长的数值出现的次数 和 偶数最多的值出现的次数。
    • 偶数最多的值出现的次数 = 奇数最多的值出现的次数。没有更小的一方,因此两边均需要取次长的计算,并将两值进行比较。
  • 注:每次选择更换最小的一方,是因为贪心的思想,保留最大的,更换最小的。
var minimumOperations = function(nums) {
  let len = nums.length;
  if(len < 2) return 0;
  // 奇数
  let mapOdd = new Map();
  // 偶数
  let mapEven = new Map();
  // 记录下来数据
  for(let i = 0; i < len; i++) {
    if(i % 2 == 0){
      if(mapEven.has(nums[i])){
        mapEven.set(nums[i], mapEven.get(nums[i]) + 1);
      }else {
        mapEven.set(nums[i], 1);
      }
    }else {
      // 奇数
      if(mapOdd.has(nums[i])){
        mapOdd.set(nums[i], mapOdd.get(nums[i]) + 1);
      }else {
        mapOdd.set(nums[i], 1);
      }
    }
  }
  // 排序
  let evenArr = [...mapEven.entries()];
  let oddArr = [...mapOdd.entries()];
  evenArr.sort((a,b) => b[1] - a[1]);
  oddArr.sort((a,b) => b[1] - a[1]);

  // console.log(evenArr,oddArr);
  // 判断情况
  let ans = 0;
  let lenOdd = oddArr.length;
  let lenEven = evenArr.length;
  let tempt;
  if(evenArr[0][0] != oddArr[0][0]){
    // 不相等
    ans = len - evenArr[0][1] - oddArr[0][1];
  }else {
    // 相等
    if(evenArr[0][1] < oddArr[0][1]){
      // 找小的的次长
      if(lenEven > 1){
        ans = len - evenArr[1][1] - oddArr[0][1];
      }else {
        // 不存在次长
        tempt = evenArr[0][1] + oddArr[0][1];
        ans = len - tempt + parseInt(tempt / 2);
      }
    }else if(evenArr[0][1] > oddArr[0][1]){
      // 奇数的次长
      if(lenOdd > 1){
        // 存在次长
        ans = len - oddArr[1][1] - evenArr[0][1];
      }else {
        // 不存在次长
        tempt = oddArr[0][1] + evenArr[0][1];
        ans = len - tempt + parseInt(tempt / 2);
      }
    }else if(evenArr[0][1] == oddArr[0][1]){
      // 比较小找哪个的次长更划算
      // 两个都存在次长
      let min = Infinity;
      if(lenOdd > 1){
        min = len - oddArr[1][1] - evenArr[0][1];
      }else if(lenEven > 1){
        min = (len - evenArr[1][1] - oddArr[0][1]) > min ? min : (len - evenArr[1][1] - oddArr[0][1]);
      }
      ans = min;
      // 两个都不存在次长
      if(lenOdd == 1 && lenEven == 1){
        ans = parseInt(len / 2);
      }
    }
  }
  return ans;
};

优化解法

  • 处理数据方面都没有优化,仍然是map和排序进行预处理。但是在判断条件方面,进行了更改。
    • 不相等的时候,同上。
    • 相等(优化)(将情况整合后,合并为如下两种情况)
      • 第一种情况:取偶数次长,奇数最长
      • 第二种情况:取奇数最长,偶数次长
// 针对上面代码判断条件的整合和修改
  if(evenArr[0][0] != oddArr[0][0]){
    // 不相等
    ans = len - evenArr[0][1] - oddArr[0][1];
  }else {
    // 相等,要么取奇1偶2要么取奇2偶1
    ans = len - Math.max((evenArr[0][1] + oddArr[1][1]),(evenArr[1][1] + oddArr[0][1])); 
  }
  return ans;
};