【LeetCode 2808】使循环数组所有元素相等的最少秒数

题目描述

原题链接: LeetCode.2808 使循环数组所有元素相等的最少秒数

解题思路

  • 每次变化可以选择变成前一个元素或后一个元素,包括 [0] 和 [n - 1] 的转化;
  • 换个角度思考,每秒最多可以有两个不同元素nums[i-1]和nums[i+1]变化成nums[i]元素;
  • 假设nums[i]元素只出现一次,想要将所有元素同化那么每秒只能同化两个,下一秒仍然只能从已同化区域的两端继续同化两个;
  • 如果nums[i]元素值出现两次,这两次中间的不同元素个数为x,那么想要将x个元素全部同化就需要"x / 2"秒;
  • 总结一下,每秒可以向两边同化2个不同元素,每个元素相邻两次出现的间距元素个数最大值除以2就是将数组全部同化的秒数;
  • 因为是循环数组,所以还要考虑首尾分段的这种特殊间距情况。

解题代码

  /**
   * 哈希表记录元素下标集合,计算间距最大值得到变化所需次数
   * 执行用时: 69 ms , 在所有 Java 提交中击败了 76.56% 的用户
   * 内存消耗: 79.25 MB , 在所有 Java 提交中击败了 45.31% 的用户
   */
  public int minimumSeconds(List<Integer> nums) {
      if (nums == null || nums.size() < 2) {
          return 0;
      }
      Map<Integer, List<Integer>> idxListMap = new HashMap<>();
      int n = nums.size();
      for (int i = 0; i < n; i++) {
          idxListMap.computeIfAbsent(nums.get(i), a -> new ArrayList<>()).add(i);
      }
      if (idxListMap.size() < 2) {
          // 原始数组元素全部相同
          return 0;
      }
      int ans = n;
      for (List<Integer> idxList : idxListMap.values()) {
          // 计算相同元素的下标间距最大值,初始化为循环数组需要考虑的首尾两个下标间距值
          int max = idxList.get(0) + n - idxList.get(idxList.size() - 1);
          for (int i = 1; i < idxList.size(); i++) {
              max = Math.max(idxList.get(i) - idxList.get(i - 1), max);
          }
          // 将数组变成当前元素值的次数为 max / 2
          ans = Math.min(ans, max / 2);
      }
      return ans;
  }

考虑到只需要计算相邻两次下标差值,没必要保存所有下标,可以进一步优化:

  /**
   * 
   * 执行用时: 47 ms , 在所有 Java 提交中击败了 93.75% 的用户
   * 内存消耗: 61.35 MB , 在所有 Java 提交中击败了 96.88% 的用户
   */
  public int minimumSeconds(List<Integer> nums) {
      if (nums == null || nums.size() < 2) {
          return 0;
      }
      /*
      * arr[0]: key第一次出现的下标
      * arr[1]: key值上一次出现的下标
      * arr[2]: key值相邻两次出现的中间元素个数+1的最大值
      */
      Map<Integer, int[]> map = new HashMap<>();
      int n = nums.size();
      for (int i = 0; i < n; i++) {
          int[] arr = map.computeIfAbsent(nums.get(i), k -> new int[3]);
          if (arr[2] == 0) {
              // 第一次出现
              arr[0] = arr[1] = i;
              arr[2] = -1;
          }
          else {
              arr[2] = Math.max(i - arr[1], arr[2]);
              arr[1] = i;
          }
      }
      if (map.size() < 2) {
          // 原始数组元素全部相同
          return 0;
      }
      int ans = n;
      for (int[] arr : map.values()) {
          // 循环数组情况下,元素第一次和最后一次出现的下标间距最大值可能由首尾两端形成
          int max = Math.max(arr[2], arr[0] + n - arr[1]);
          // 将数组变成当前元素值的次数为 max / 2
          ans = Math.min(ans, max / 2);
      }
      return ans;
  }

posted on 2024-01-30 13:07  真不如烂笔头  阅读(11)  评论(0编辑  收藏  举报

导航