Implementation
- why not build result with condition
i < j < p < q
?
Algorithm
- code builds hash map as combination of all indexes from
nums
. Combination of all unique values from nums
(or index or unique values) is better choice. Case: fourSum([0 for x in range(n)], 0)
- code builds hash map with integers from
nums
which can't be added to result. Case: fourSum([x for x in range(1, n, 1)], 0)
- code check if for
key
from hash map also target - key
exists in final loop, can earlier. Case: fourSum([x for x in range(0, n*10, 10)], n*5+1)
- You can split hash map for two parts:
a,b
and c,d
pair. Don't change complexity of hash map, but final loop: 1/2 * 1/2 faster
Speedup
- best: algorithm (big O notation), e.g. reduce O(n^2) memory to O(n)
- sometimes good: algorithm constants, e.g. split hash map for first and second pair
- bad: dirty, low-level language speed-up constants, e.g. replace
itertools.combinations
with directly loops. This is anti-pattern. Reasons: less understandable, maintainable, changeable and paradoxically slower. Slower because bottlenecks are usually caused by cascade several algorithms, e.g. O(n^3) * O(n^3). With clean code easier reduce problem to O(n^5) or less. With dirty code usually at the end we get O(n^6) with small const
Code (the same O(n^2) mem)
from itertools import combinations from collections import defaultdict, Counter def fourSum(self, nums, target): if len(nums) < 4: return [] half_target = target // 2 counter = Counter(nums) uniques_wide = sorted(counter) x_min, x_max = target - 3 * uniques_wide[-1], target - 3 * uniques_wide[0] # bad uniques = [ x for x in uniques_wide if x_min <= x <= x_max ] duplicates = [x for x in uniques if counter[x] > 1] target_minus_xy_sums = set(target - x - y for x, y in combinations(uniques, 2)) target_minus_xy_sums |= set(target - x - x for x in duplicates) ab_sum_pairs, cd_sum_pairs = defaultdict(list), defaultdict(list) for (x, y) in combinations(uniques, 2): if x + y in target_minus_xy_sums: if x + y <= half_target: ab_sum_pairs[x + y].append((x, y)) if x + y >= half_target: cd_sum_pairs[x + y].append((x, y)) for x in duplicates: if x + x in target_minus_xy_sums: if x + x <= half_target: ab_sum_pairs[x + x].append((x, x)) if x + x >= half_target: cd_sum_pairs[x + x].append((x, x)) return [[a, b, c, d] for ab in ab_sum_pairs for (a, b) in ab_sum_pairs[ab] for (c, d) in cd_sum_pairs[target - ab] if b < c or b == c and [a, b, c, d].count(b) <= counter[b]] # if bi < ci