列表

详情


1982. 从子集的和还原数组

存在一个未知数组需要你进行还原,给你一个整数 n 表示该数组的长度。另给你一个数组 sums ,由未知数组中全部 2n子集的和 组成(子集中的元素没有特定的顺序)。

返回一个长度为 n 的数组 ans 表示还原得到的未知数组。如果存在 多种 答案,只需返回其中 任意一个

如果可以由数组 arr 删除部分元素(也可能不删除或全删除)得到数组 sub ,那么数组 sub 就是数组 arr 的一个 子集sub 的元素之和就是 arr 的一个 子集的和 。一个空数组的元素之和为 0

注意:生成的测试用例将保证至少存在一个正确答案。

 

示例 1:

输入:n = 3, sums = [-3,-2,-1,0,0,1,2,3]
输出:[1,2,-3]
解释:[1,2,-3] 能够满足给出的子集的和:
- []:和是 0
- [1]:和是 1
- [2]:和是 2
- [1,2]:和是 3
- [-3]:和是 -3
- [1,-3]:和是 -2
- [2,-3]:和是 -1
- [1,2,-3]:和是 0
注意,[1,2,-3] 的任何排列和 [-1,-2,3] 的任何排列都会被视作正确答案。

示例 2:

输入:n = 2, sums = [0,0,0,0]
输出:[0,0]
解释:唯一的正确答案是 [0,0] 。

示例 3:

输入:n = 4, sums = [0,0,5,5,4,-1,4,9,9,-1,4,3,4,8,3,8]
输出:[0,-1,4,5]
解释:[0,-1,4,5] 能够满足给出的子集的和。

 

提示:

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class Solution { public: vector<int> recoverArray(int n, vector<int>& sums) { } };

rust 解法, 执行用时: 472 ms, 内存消耗: 2.4 MB, 提交时间: 2023-10-10 23:32:04

impl Solution {
    pub fn recover_array(n: i32, mut sums: Vec<i32>) -> Vec<i32> {
        let mut res = Vec::new();
        sums.sort_unstable();
        for i in 1..=n {
            let (d, mut l, mut r) = (sums[1] - sums[0], vec![0; sums.len() / 2], vec![0; sums.len() / 2]);
            for j in 1..=l.len() {
                let (u, v) = (sums.last().unwrap() - d, sums.pop().unwrap());
                sums.remove(sums.binary_search(&u).unwrap());
                l[r.len() - j] = u;
                r[l.len() - j] = v;
            }
            res.push(if l.contains(&0) { d } else { -d });
            std::mem::swap(&mut sums, if l.contains(&0) { &mut l } else { &mut r });
        }
        res
    }
}

rust 解法, 执行用时: 48 ms, 内存消耗: 2.4 MB, 提交时间: 2023-10-10 23:31:52

impl Solution {
    pub fn recover_array(n: i32, mut sums: Vec<i32>) -> Vec<i32> {
        let mut res = Vec::new();
        sums.sort_unstable();
        let len = sums.len();
        let mut sums2 = vec![0; len / 2];
        let mut vis = vec![false; len];
        for i in 0..n {
            let sz = len >> i;
            for j in 0..sz { vis[j] = false; }
            let (mut p1, mut pos, cand) = (0, 0, sums[1] - sums[0]);
            for j in 0..sz {
                if vis[j] { continue; }
                pos = usize::max(pos, j) + 1;
                let targ = sums[j] + cand;
                while sums[pos] != targ { pos += 1; }
                vis[pos] = true;
                sums[p1] = sums[j];
                sums2[p1] = sums[pos];
                p1 += 1;
            }
            let flg = sums[0..sz / 2].contains(&0);
            if !flg { std::mem::swap(&mut sums, &mut sums2); }
            res.push(if flg { cand } else { -cand });
        }
        res
    }
}

java 解法, 执行用时: 62 ms, 内存消耗: 53 MB, 提交时间: 2023-10-10 23:31:03

class Solution {
    public static int[] recoverArray(int n, int[] sums) {
        Arrays.sort(sums);
        int[] ans = new int[n];
        int cur = 0;
        for (int i = n; i >= 2; --i) {
            int d = sums[1] - sums[0];
            int left = 0, right = 0;
            int[] s = new int[1 << (i - 1)];
            int[] t = new int[1 << (i - 1)];
            int si = 0, ti = 0;
            boolean flag = false;
            boolean[] used = new boolean[1 << i];
            while (true) {
                while (left < (1 << i) && used[left]) {
                    ++left;
                }
                if (left == 1 << i) {
                    break;
                }
                s[si++] = sums[left];
                if (sums[left] == 0) {
                    flag = true;
                }
                used[left] = true;
                while (used[right] || sums[right] != sums[left] + d) {
                    ++right;
                }
                t[ti++] = sums[right];
                used[right] = true;
            }
            if (flag) {
                ans[cur++] = d;
                sums = s;
            } else {
                ans[cur++] = -d;
                sums = t;
            }
        }
        ans[cur++] = sums[0] + sums[1];
        return ans;
    }
}

cpp 解法, 执行用时: 204 ms, 内存消耗: 108.5 MB, 提交时间: 2023-10-10 23:30:29

class Solution {
public:
    vector<int> recoverArray(int n, vector<int>& sums) {
        // 提前将数组排好序
        sort(sums.begin(), sums.end());

        vector<int> ans;
        for (int i = n; i >= 2; --i) {
            int d = sums[1] - sums[0];
            // 双指针构造 s 和 t
            int left = 0, right = 0;
            vector<int> s, t;
            // 记录每个子集和是否选过
            vector<int> used(1 << i);
            while (true) {
                // left 指针找到最小的未被选择过的子集和
                while (left < (1 << i) && used[left]) {
                    ++left;
                }
                if (left == (1 << i)) {
                    break;
                }
                s.push_back(sums[left]);
                used[left] = true;
                // right 指针找到 sums[left] + d
                while (used[right] || sums[right] != sums[left] + d) {
                    ++right;
                }
                t.push_back(sums[right]);
                used[right] = true;
            }
            if (find(s.begin(), s.end(), 0) != s.end()) {
                // 包含 d 并求解 (n-1, s)
                ans.push_back(d);
                sums = move(s);
            }
            else {
                // 包含 -d 并求解 (n-1, t)
                ans.push_back(-d);
                sums = move(t);
            }
        }
        // 迭代到 n=1 时,数组中必然一个为 0,另一个为剩下的最后一个数
        ans.push_back(sums[0] + sums[1]);
        return ans;
    }
};

python3 解法, 执行用时: 728 ms, 内存消耗: 22.5 MB, 提交时间: 2023-10-10 23:30:15

class Solution:
    def recoverArray(self, n: int, sums):
        # 提前将数组排好序
        sums.sort()

        ans = list()
        for i in range(n, 1, -1):
            d = sums[1] - sums[0]
            # 双指针构造 s 和 t
            left = right = 0
            s, t = list(), list()
            # 记录每个子集和是否选过
            used = set()
            while True:
                # left 指针找到最小的未被选择过的子集和
                while left < (1 << i) and left in used:
                    left += 1
                if left == (1 << i):
                    break
                s.append(sums[left])
                used.add(left)
                # right 指针找到 sums[left] + d
                while right in used or sums[right] != sums[left] + d:
                    right += 1
                t.append(sums[right])
                used.add(right)

            if 0 in s:
                # 包含 d 并求解 (n-1, s)
                ans.append(d)
                sums = s
            else:
                # 包含 -d 并求解 (n-1, t)
                ans.append(-d)
                sums = t

        # 迭代到 n=1 时,数组中必然一个为 0,另一个为剩下的最后一个数
        ans.append(sums[0] + sums[1])
        return ans

python3 解法, 执行用时: 1196 ms, 内存消耗: 24.5 MB, 提交时间: 2023-10-10 23:30:01

class Solution:
    def recoverArray(self, n: int, sums):
        # n 个数构成程度为 2^n 的子集和数组 sums
        # 返回值为空表示无解,否则表示有解
        def dfs(n: int, sums: List[int]) -> List[int]:
            # 递归到 n=1 时,数组中必然一个为 0,另一个为剩下的最后一个数
            # 如果满足该要求,返回剩下的最后一个数,否则返回表示无解的空数组
            if n == 1:
                if sums[0] == 0:
                    return [sums[1]]
                if sums[1] == 0:
                    return [sums[0]]
                return []

            d = sums[1] - sums[0]
            # 双指针构造 s 和 t
            left = right = 0
            s, t = list(), list()
            # 记录每个子集和是否选过
            used = set()
            while True:
                # left 指针找到最小的未被选择过的子集和
                while left < (1 << n) and left in used:
                    left += 1
                if left == (1 << n):
                    break
                s.append(sums[left])
                used.add(left)
                # right 指针找到 sums[left] + d
                while right in used or sums[right] != sums[left] + d:
                    right += 1
                t.append(sums[right])
                used.add(right)

            # 尝试包含 d 并递归求解 (n-1, s)
            ans = dfs(n - 1, s)
            if ans:
                return ans + [d]

            # 尝试包含 -d 并递归求解 (n-1, t)
            ans = dfs(n - 1, t)
            if ans:
                return ans + [-d]
            
            # 无解返回空数组
            return []
        
        # 提前将数组排好序
        sums.sort()
        return dfs(n, sums)

cpp 解法, 执行用时: 384 ms, 内存消耗: 151.8 MB, 提交时间: 2023-10-10 23:29:49

class Solution {
private:
    // n 个数构成程度为 2^n 的子集和数组 sums
    // 返回值为空表示无解,否则表示有解
    vector<int> dfs(int n, vector<int>& sums) {
        // 递归到 n=1 时,数组中必然一个为 0,另一个为剩下的最后一个数
        // 如果满足该要求,返回剩下的最后一个数,否则返回表示无解的空数组
        if (n == 1) {
            if (sums[0] == 0) {
                return {sums[1]};
            }
            if (sums[1] == 0) {
                return {sums[0]};
            }
            return {};
        }

        int d = sums[1] - sums[0];
        // 双指针构造 s 和 t
        int left = 0, right = 0;
        vector<int> s, t;
        // 记录每个子集和是否选过
        vector<int> used(1 << n);
        while (true) {
            // left 指针找到最小的未被选择过的子集和
            while (left < (1 << n) && used[left]) {
                ++left;
            }
            if (left == (1 << n)) {
                break;
            }
            s.push_back(sums[left]);
            used[left] = true;
            // right 指针找到 sums[left] + d
            while (used[right] || sums[right] != sums[left] + d) {
                ++right;
            }
            t.push_back(sums[right]);
            used[right] = true;
        }

        // 尝试包含 d 并递归求解 (n-1, s)
        vector<int> ans = dfs(n - 1, s);
        if (!ans.empty()) {
            ans.push_back(d);
            return ans;
        }
        // 尝试包含 -d 并递归求解 (n-1, t)
        ans = dfs(n - 1, t);
        if (!ans.empty()) {
            ans.push_back(-d);
            return ans;
        }
        // 无解返回空数组
        return {};
    }

public:
    vector<int> recoverArray(int n, vector<int>& sums) {
        // 提前将数组排好序
        sort(sums.begin(), sums.end());
        return dfs(n, sums);
    }
};

上一题