Problem overview
The function contains receives a slice of integers, nums, and an integer value. It returns true if value appears anywhere in nums and false otherwise. The lesson asks you to write the search yourself rather than call a library function such as slices.Contains.
For contains([]int{1, 2, 4, 5}, 4) the answer is true, because 4 is the third element. For contains([]int{-1, 2, -4, 0, 10}, 7) the answer is false: none of the five elements equals 7.
Approach: look at every element until one matches
An unsorted slice gives no hint about where a value might be, so the only way to be sure it is missing is to look at every element. This is a linear search. Walk through the slice from the start and compare each element with value. The first time they are equal, the answer is true, and there is no reason to keep looking. If the walk ends without a match, the answer is false.
The order of the two returns matters. true can be decided in the middle of the loop, but false can only be decided after the loop has looked at everything. That is why return false sits after the loop and never inside it.
Step by step
- Loop over the slice with
for ... range, naming the elementnum. The loop gives each element in turn, from the first to the last. - Compare inside the loop. If
num == value, returntrueimmediately. Returning ends the whole function, so the remaining elements are never examined. - Return
falseafter the loop. This line is reached only when no element matched, so noelseis needed.
The solution in Go
func contains(nums []int, value int) bool {
for _, num := range nums {
if num == value {
return true
}
}
return false
}
The Go details that matter here:
rangeyields an index and a value. Only the value is needed, so the index is assigned to the blank identifier_. Go treats an unused variable as a compile error, so naming it and ignoring it would not compile.numis a copy. Each pass copies the element intonum. That is harmless for a read-only search.==works directly onint. No special method is needed for numbers.- Every path returns. A function declared to return
boolmust end in a return statement. The finalreturn falsecovers the case where the loop finishes, and it also covers an empty ornilslice, where the loop body never runs.
Complexity
Time: O(n). In the worst case, the value is last or missing, and the loop makes one comparison per element. In the best case the first element matches and the loop stops at once.
Space: O(1). The function uses only the loop variable. It does not copy the slice, since a slice passed to a function shares the same underlying elements.
Edge cases and common mistakes
- Empty slice.
contains([]int{}, 5)isfalse, and so iscontains(nil, 5). Ranging over either runs the body zero times. - Returning false inside the loop. Writing
else { return false }after the comparison would stop at the first element that doesn't match.contains([]int{1, 2, 4, 5}, 4)would wrongly givefalse, because 1 is not 4. - Negative numbers and zero. They compare like any other integer.
contains([]int{-1, 2, -4, 0, 10}, 0)istrue. - Duplicates. The function stops at the first match, so repeated values cost nothing extra.
- Counting instead of stopping. A counter plus a final check also works but keeps scanning after the answer is known. The early return is shorter and faster.
Testing
contains([]int{1, 2, 4, 5}, 4)givestrue.contains([]int{-1, 2, -4, 0, 10}, 7)givesfalse.contains([]int{}, 1)givesfalse.contains([]int{3}, 3)givestrue.