Problem overview
The function dot receives two sparse vectors, first and second, and returns their dot product: the sum of A[k] * B[k] over every position k. A sparse vector is mostly zeros, so it is stored as a list of [index, value] pairs, one per nonzero position, in increasing order of index. In Go that is a [][]int: each inner slice has the index at position 0 and the value at position 1.
In the example, first = [[0, 6], [3, 9], [4, 7]] stands for [6, 0, 0, 9, 7] and second = [[1, 3], [3, 4]] stands for [0, 3, 0, 4, 0]. Only position 3 is nonzero in both vectors, so the dot product is 9 * 4 = 36.
Approach: walk both lists at once
The obvious route rebuilds both full vectors and multiplies them position by position. That works, but its cost depends on the vectors' length rather than on how many nonzero entries they have, and the whole point of a sparse format is to avoid touching the zeros.
A product contributes to the sum only when both vectors are nonzero at the same position. Because each list is sorted by index, finding those shared positions is the same job as merging two sorted lists. Keep one pointer in each list and compare the indices they point at:
- If the index in
firstis smaller,secondhas an implicit 0 there. That product is 0, so skip the entry infirst. - If the index in
secondis smaller, skip the entry insecondfor the same reason. - If the indices are equal, multiply the two values, add the product to the total, and move past both entries.
Every step moves at least one pointer forward, and each pointer only moves forward, so the loop ends after at most len(first) + len(second) steps.
Tracing the example: the pointers start at [0, 6] and [1, 3]. Index 0 is smaller, so the first pointer moves to [3, 9]. Now 3 is larger than 1, so the second pointer moves to [3, 4]. The indices match: 9 * 4 = 36 goes into the total and both pointers advance. The second list is used up, so the loop stops. The entry [4, 7] is never needed, because second has nothing at or beyond index 4.
Step by step
- Declare the pointers and the total on one line:
iandjare positions in the two lists, andresultcollects the sum. All three start at 0. - Loop while both pointers are in range. Go's only loop keyword is
for, andfor cond { ... }plays the role ofwhile. Once either list runs out, no further pair of indices can match, so stopping there is correct. - Handle the case where
firstis behind by advancingi. - Handle the mirror case with an
else ifthat advancesj. - Handle equal indices in the final
else: add the product of the two stored values and advance both pointers. Advancing only one would compare the same index again and count it twice. - Return
resultafter the loop. Whatever is left in the longer list pairs with implicit zeros.
The solution in Go
func dot(first, second [][]int) int {
i, j, result := 0, 0, 0
for i < len(first) && j < len(second) {
if first[i][0] < second[j][0] {
i++
} else if first[i][0] > second[j][0] {
j++
} else {
result += first[i][1] * second[j][1]
i++
j++
}
}
return result
}
A few Go details:
- Multiple assignment with
:=.i, j, result := 0, 0, 0declares threeintvariables in one statement. Go reports an unused local variable as a compile error, and all three are used. - A loop condition without parentheses. Go does not wrap the condition of
fororifin parentheses, but the braces are mandatory. Theelse ifandelsekeywords must sit on the same line as the closing brace before them. first[i][0]versusfirst[i][1].first[i]is a slice holding one pair, so the extra subscript picks the index (0) or the value (1).++is a statement. In Go,i++cannot be used inside an expression such asa[i++], so each advance gets its own line.
Complexity
Time: O(n + m), where n and m are the numbers of nonzero entries in the two vectors. Each iteration advances at least one pointer, and the pointers never move back.
Space: O(1). The function keeps three integers and does not copy the lists.
Edge cases and common mistakes
- An empty list. If either vector has no nonzero entries, the loop body never runs and the function returns 0. This includes
nilslices, becauselen(nil)is 0. - No shared position.
[[2, 5]]and[[3, 5]]never match, so the result is 0, even though both vectors are nonzero. - Negative values. Products can be negative:
[[0, 2], [5, -3]]with[[0, 4], [5, 2], [9, 1]]gives8 + (-6) = 2. - Forgetting to advance both pointers on a match. The loop then compares the same two entries forever and never ends.
- Comparing values instead of indices. The comparisons use element 0 of each pair. Element 1 is only for the product.
- Integer overflow.
intis 64 bits, and a product or sum beyond that wraps around silently instead of raising an error. Ordinary inputs stay far from that limit.
Testing
first = [[0, 6], [3, 9], [4, 7]],second = [[1, 3], [3, 4]]gives36.first = [],second = [[1, 3]]gives0.first = [[2, 5]],second = [[3, 5]]gives0.first = [[0, 2], [5, -3]],second = [[0, 4], [5, 2], [9, 1]]gives2.