一起刷力扣之【954. 二倍数对数组】

143 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

题目描述

给定一个长度为偶数的整数数组 arr,只有对 arr 进行重组后可以满足 “对于每个 0 <= i < len(arr) / 2,都有 arr[2 * i + 1] = 2 * arr[2 * i]” 时,返回 true;否则,返回 false

示例

输入: arr = [3,1,3,6]
输出: false
输入: arr = [-1,2]
输出: false
输入:arr = [4,-2,2,-4]
输出:true
解释:可以用 [-2,-4][2,4] 这两组组成 [-2,-4,2,4] 或是 [2,4,-2,-4]

提示

  • 0 <= arr.length <= 3 * 104^4
  • arr.length 是偶数
  • -105^5 <= arr[i] <= 105^5

优先队列 + 哈希表

这道题的描述可能第一眼看那些公式啥的感觉好长,好像很难的样子,实际上拆解一下,你会发现原来这这么简单。

  1. 0 <= i < len(arr) / 2,通过这里可以得到一个长度n = len(arr) / 2
  2. arr[2 * i + 1] = 2 * arr[2 * i],去掉括号中的相同的条件,可以得到arr[i + 1] = 2 * arr[i]
  3. 结合上面两个小点,我们可以得知该题要判断的是当前数组中一半的元素是否能在数组中找到等于其值两倍的元素
  4. 由于要将数组中的元素进行两两匹配,那么我们需要将数组进行排序,取其中值较小的一半元素,才可在另一半的数组元素中尝试寻找匹配值
  5. 按照上面的提示3给出的数值边界,元素还有正负值区分,那么在排序时我们还需要考虑,当元素大于0时,我们需要的是较小元素;当元素小于0时,我们则需要的是较大的元素,以此为基础来找到匹配的元素

GIF 2022-4-2 9-14-31.gif

class Solution {
    public boolean canReorderDoubled(int[] arr) {
        int n = arr.length;
        // 定义优先队列,将元素按照绝对值大小进行排序
        PriorityQueue<Integer> queue = new PriorityQueue<>((a, b) -> Math.abs(a) - Math.abs(b));
        // 保存每个值出现的次数
        Map<Integer, Integer> map = new HashMap<>(n);

        // 初始化
        for(int a : arr){
            queue.add(a);
            map.put(a, map.getOrDefault(a, 0) + 1);
        }

        // 遍历所有元素,找到匹配值,由于有负数的存在,这里不能将终止条件设置为queue.size() > n / 2
        while(!queue.isEmpty()){
            int num = queue.poll();
            // 如果num还有剩余,表示还需要进行匹配
            if(map.get(num) > 0){
                map.put(num, map.get(num) - 1);
                int tmp = num * 2;
                // 数量不足,不满足二倍数对数组条件
                if(map.getOrDefault(tmp, 0) == 0){
                    return false;
                }
                map.put(tmp, map.get(tmp) - 1);
            }
        }

        return true;
    }
}

哈希表 + 排序

在上面的方法中,我们需要将数组所有元素都完整遍历一次,这对于哈希表来说无疑会多了很多重复的步骤,浪费是可耻的,我们要将哈希表中统计的结果利用起来。

  1. 统计数组中所有元素出现的次数
  2. 得到出现过的数值,并将其按照绝对值从小到大进行排序
  3. 遍历出现过的数值,判断是否有足够多的其值两倍的元素
  4. 更新统计结果
class Solution {
    public boolean canReorderDoubled(int[] arr) {
        // 统计数组中所有元素出现的次数
         Map<Integer, Integer> map = new HashMap<>();
        for(int a : arr){
            map.put(a, map.getOrDefault(a, 0) + 1);
        }

        // 得到出现过的数值,并将其按照绝对值从小到大进行排序
        List<Integer> list = new ArrayList<>(map.size());
        for(int key : map.keySet()){
            list.add(key);
        }
        Collections.sort(list, (a, b) -> Math.abs(a) - Math.abs(b));

        for(int num : list){
            int tmp = 2 * num;
            int value = map.get(num);
            // 判断是否有足够多的**其值两倍**的元素
            if(map.getOrDefault(tmp, 0) < value){
                return false;
            }
            // 更新统计结果
            map.put(tmp, map.getOrDefault(tmp, 0) - value);
        }

        return true;
    }
}