Given nums of length , return an array of length that is nums
followed by nums again.
[]int{1, 3, 2, 1} // → [1 3 2 1 1 3 2 1]The algorithm is not the point. Everything interesting here is about how Go slices actually behave, and this is the smallest problem where you can see it.
The solution
func getConcatenation(nums []int) []int { var size = len(nums) var ans = make([]int, 2*size)
for i, v := range nums { ans[i] = v ans[size+i] = v }
return ans}One pass over nums, two writes per iteration:
nums[0] is written to ans[0] and ans[4 + 0] in the same iteration. Both destinations
already exist — nothing grows, nothing is copied to make room, and the loop runs exactly
4 times rather than 8.
time, space — and the space is the output, so there is nothing extra.
Say the length or say the capacity, not neither
Three spellings, and the difference is not stylistic:
ans := make([]int, 2*size) // length 2n, all zero — index into itans := make([]int, 0, 2*size) // length 0, capacity 2n — append into itans := []int{} // length 0, capacity 0 — append and realloccopy says it better
The loop is fine. The standard library states the intent in two lines:
func getConcatenation(nums []int) []int { size := len(nums) ans := make([]int, 2*size)
copy(ans, nums) copy(ans[size:], nums)
return ans}copy moves as many elements as fit in the shorter of the two slices and
returns that count. For a slice of a basic type it compiles down to a bulk
memory move rather than an element-at-a-time loop, so it is also faster than
the version above — the gap widens with n.
ans[size:] is a view onto the second half of the same array, not a new
one. No allocation happens on that line.
The one-liner, and why it is a trap
return append(nums, nums...)This produces the right answer on LeetCode. It can also corrupt data belonging to the caller.
What it is actually teaching
The concatenation is a pretext. The habits are the point, and they carry to every array problem you will write:
- Know the output size before you start, and allocate once.
- Prefer
copyto a loop when you are moving a run of elements — it is clearer and it is faster. - Never assume a slice parameter is yours to grow, sort or reorder.
The same three show up in merge sort’s merge step, which allocates its output up front for exactly these reasons.