class SparseVector:
def __init__(self, nums):
# Store non-zero elements as {index: value}
self.nonzero = {i: num for i, num in enumerate(nums) if num != 0}
def dotProduct(self, vec):
# Only iterate over the smaller set of non-zero elements
if len(self.nonzero) > len(vec.nonzero):
return vec.dotProduct(self)
res = 0
for i, v in self.nonzero.items():
res += v * vec.nonzero.get(i, 0)
return res
class SparseVector {
public:
unordered_map<int, int> nonzero;
SparseVector(vector<int> &nums) {
for (int i = 0; i < nums.size(); ++i) {
if (nums[i] != 0) {
nonzero[i] = nums[i];
}
}
}
int dotProduct(SparseVector &vec) {
// Iterate over the smaller map
if (nonzero.size() > vec.nonzero.size()) {
return vec.dotProduct(*this);
}
int res = 0;
for (auto &p : nonzero) {
int idx = p.first;
int val = p.second;
if (vec.nonzero.count(idx)) {
res += val * vec.nonzero.at(idx);
}
}
return res;
}
};
class SparseVector {
private Map<Integer, Integer> nonzero;
public SparseVector(int[] nums) {
nonzero = new HashMap<>();
for (int i = 0; i < nums.length; ++i) {
if (nums[i] != 0) {
nonzero.put(i, nums[i]);
}
}
}
// Return the dotProduct of two sparse vectors
public int dotProduct(SparseVector vec) {
if (this.nonzero.size() > vec.nonzero.size()) {
return vec.dotProduct(this);
}
int res = 0;
for (Map.Entry<Integer, Integer> entry : this.nonzero.entrySet()) {
int idx = entry.getKey();
int val = entry.getValue();
res += val * vec.nonzero.getOrDefault(idx, 0);
}
return res;
}
}
class SparseVector {
constructor(nums) {
this.nonzero = new Map();
for (let i = 0; i < nums.length; ++i) {
if (nums[i] !== 0) {
this.nonzero.set(i, nums[i]);
}
}
}
dotProduct(vec) {
// Iterate over the smaller map
let a = this.nonzero, b = vec.nonzero;
if (a.size > b.size) {
return vec.dotProduct(this);
}
let res = 0;
for (let [i, v] of a) {
res += v * (b.get(i) || 0);
}
return res;
}
}
You are given two vectors represented as integer arrays, nums1
and nums2
, that can be very large but are sparse (most elements are zero). Your task is to efficiently compute their dot product, which is defined as the sum of products of corresponding elements: nums1[0]*nums2[0] + nums1[1]*nums2[1] + ...
.
The challenge is to avoid unnecessary computation and memory usage by taking advantage of the sparsity. You should design a class SparseVector
that supports:
dotProduct
method that takes another SparseVector
and returns the dot product as an integerAt first glance, you might consider iterating over both arrays and multiplying corresponding elements. However, since most elements are zero, this would waste both time and memory. Instead, notice that only the positions where both vectors are non-zero matter for the dot product.
We can exploit this sparsity by storing only the non-zero elements and their indices. This way, we can skip all the zero multiplications, which do not affect the result. If we store non-zero elements in a hash map (index → value
), we can quickly check if the other vector also has a non-zero value at that index.
To further optimize, we can iterate over the smaller set of non-zero indices to minimize the number of lookups and multiplications.
Here’s a step-by-step plan to solve the problem efficiently:
SparseVector
, scan through the input array and record each non-zero element and its index in a hash map or dictionary. This reduces storage and allows fast lookups.
This approach is efficient because:
Example:
nums1 = [1, 0, 0, 2, 3]
nums2 = [0, 3, 0, 4, 0]
SparseVector(nums1)
stores: {0: 1, 3: 2, 4: 3}SparseVector(nums2)
stores: {1: 3, 3: 4}Brute-force Approach:
The optimized approach is significantly faster and more memory-efficient for large, sparse vectors.
By representing sparse vectors with hash maps of non-zero elements, we can compute their dot product efficiently by iterating only over the relevant indices. This avoids unnecessary computation and leverages the data's sparsity. The approach is elegant because it reduces both time and space complexity, making it suitable for very large vectors with few non-zero entries.