leetcode-二倍数对数组

677 阅读2分钟

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

连续几天忙着公司的事情,没空刷leetcode。终于到了一个3天小长假,新的起点大家继续加油吧。

题目描述

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

示例 1: 输入: arr = [3,1,3,6] 输出: false

示例 2: 输入: arr = [2,1,2,6] 输出: false

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

思路

题目描述的有点绕,翻译成大白话,就是要把一个arr平分成2个部分,每个部分都有len/2个元素,第2部分每个元素恰好是第1部分每个元素值的2倍。
比较直观的想法是,遍历arr,对于每个元素item,尝试看item/2或者item2是否在数组中,这个操作可以使用哈希表在O(1)的时间复杂度完成,如果存在,就去掉这一对,再用这个方法继续判断剩下的元素,直到arr的元素全部用完(返回true)或者某个item,数组中不存在item/2或者item2(返回false)。
不过这个直观的方法,我们可以发现一个问题,如果item/2和item2在arr中都存在且没有被使用,我们无法判断应该去配对哪一个。例如这样一个arr:[1,2,2,4],其中有2个2:1个2要作为大数跟1配对,另外1个2要作为小数跟4配对。
所以,我们可以改进上面的方法,对原始数组arr,先按照绝对值从小到大排序,这时候,绝对值最小的数min不可能作为item
2,因为不存在绝对值比它更小的数了,这时候,只要去arr中找min2即可,如果存在,则这2个数进行配对,然后重复刚刚的方法继续去寻找当前绝对值最小的数,直到数字全部用完(返回true)或者找不到min2(返回false)。
因为在arr中,数据可以重复,所以我们定义一个map,键为数值,值为重复次数,用上面的思路来编码即可。

Java版本代码

class Solution {
    public boolean canReorderDoubled(int[] arr) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int num : arr) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        List<Integer> list = map.keySet().stream().collect(Collectors.toList());
        list.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Math.abs(o1) - Math.abs(o2);
            }
        });
        for (Integer num : list) {
            int countNum = map.getOrDefault(num, 0);
            if (countNum == 0) {
                continue;
            }
            int countNum2 = map.getOrDefault(num * 2, 0);
            if (countNum2 < countNum) {
                return false;
            }
            map.put(num * 2, countNum2 - countNum);
        }
        return true;
    }
}