每日一题 - 954. 二倍数对数组

123 阅读1分钟

ttps://leetcode-cn.com/problems/array-of-doubled-pairs/

难度:中等

给定一个长度为偶数的整数数组 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]

提示:

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

方法一、Hash + 排序

/**
 * Hash做法 + 排序
 * @param arr
 * @return
 */
public static boolean canReorderDoubled(int[] arr) {
    // 需要排序,从小打大
    Arrays.sort(arr);
    // key 存储值, value存储个数
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < arr.length; i++) {
        // 值是0,直接舍掉
        if (arr[i] == 0) {
            continue;
        }
        map.put(arr[i], map.getOrDefault(arr[i], 0) + 1);
        // 注意条件。其梳理必须大于0
        if (map.containsKey(2 * arr[i]) && map.get(2 * arr[i]) > 0) {
            // 存在 2 倍的 值
            map.put(2 * arr[i], map.get(2 * arr[i]) - 1);
            map.put(arr[i], map.get(arr[i]) - 1);
        } else if (arr[i] % 2 == 0 && map.containsKey(arr[i] / 2) && map.get(arr[i] / 2) > 0) {
            // 存在 1 / 2 的值
            map.put(arr[i] / 2, map.get(arr[i] / 2) - 1);
            map.put(arr[i], map.get(arr[i]) - 1);
        }
    }
    for (Integer value : map.values()) {
        if (value != 0) {
            return false;
        }
    }
    return true;
}

方法二、回溯算法

/**
 * 回溯算法,超时了
 * 讲这些组合全部排列出来,找出满足条件的,直接返回
 *
 * 但是由于  0 <= arr.length <= 3 * 104   数组的长度太大了。结果超时了
 *
 * @param arr
 * @return
 */
public boolean canReorderDoubled2(int[] arr) {
    Arrays.sort(arr);
    List<Integer> tmp = new ArrayList<>();
    // 保证当前数,不会重复出现
    boolean[] visited = new boolean[arr.length];
    return backTrace(arr, visited, tmp);
}

boolean backTrace(int[] arr, boolean[] visited, List<Integer> tmp) {
    if (tmp.size() == arr.length) {
        return true;
    }
    for (int i = 0; i < arr.length; i++) {
        int num = arr[i];
        if (visited[i] || (tmp.size() > 0 && tmp.size() % 2 != 0 && 2 * tmp.get(tmp.size() - 1) != num)) {
            continue;
        }
        tmp.add(num);
        visited[i] = true;
        boolean res = backTrace(arr, visited, tmp);
        if (res) {
            return true;
        }
        visited[i] = false;
        tmp.remove(tmp.size() - 1);
    }
    return false;
}

官方答案:

class Solution {
    public boolean canReorderDoubled(int[] arr) {
        Map<Integer, Integer> cnt = new HashMap<Integer, Integer>();
        for (int x : arr) {
            cnt.put(x, cnt.getOrDefault(x, 0) + 1);
        }
        if (cnt.getOrDefault(0, 0) % 2 != 0) {
            return false;
        }

        List<Integer> vals = new ArrayList<Integer>();
        for (int x : cnt.keySet()) {
            vals.add(x);
        }
        Collections.sort(vals, (a, b) -> Math.abs(a) - Math.abs(b));

        for (int x : vals) {
            if (cnt.getOrDefault(2 * x, 0) < cnt.get(x)) { // 无法找到足够的 2x 与 x 配对
                return false;
            }
            cnt.put(2 * x, cnt.getOrDefault(2 * x, 0) - cnt.get(x));
        }
        return true;
    }
}

my_java_algorithm/Top0954_middle.java at main · liufei96/my_java_algorithm (github.com)

 

总结:

  • 下次在遇到这种题目,先看下数组的长度,对于这种范围的,用回溯算法,很明显是会超时。