Go语言切片咨询:两种去重子集实现的代码差异及底层原理
Great question—let’s break down exactly why these implementations behave differently, focusing on how Go handles slices, since that’s the root of your confusion.
First Implementation: Full Slice Expression vs. Basic Truncation
Your first backtracking approach uses tempList = tempList[:len(tempList)-1:len(tempList)-1] to "undo" appends during backtracking. Let’s compare this to the simpler tempList[:len(tempList)-1] and why one works while the other fails.
How Go Slices Work Under the Hood
A Go slice is made up of three core components:
- A pointer to an underlying array
- A
len(number of elements currently in use) - A
cap(total space available in the underlying array)
When you use tempList[:len(tempList)-1], you’re creating a new slice that shares the same underlying array as the original. The new slice’s len is reduced by 1, but its cap stays identical to the original slice’s capacity.
Why tempList[:len(tempList)-1] Breaks Your Code
Here’s the critical issue: when you append tempList to result with *result = append(*result, tempList), you’re storing a reference to the slice (and its underlying array) in result. If you later modify that underlying array (by appending to a slice that shares it), every reference to that array will see the changes.
For example:
- You append
[1,2]toresult—this is a reference to the underlying array holding[1,2,...]. - You truncate
tempListto[1]usingtempList[:1]—this still points to the same array. - You append
3totempList, which writes to the second position of the underlying array (since cap is still large enough), turning it into[1,3,...]. - The
[1,2]you stored inresultnow becomes[1,3]because it’s referencing the same array—corrupting your final output.
Why tempList[:len(tempList)-1:len(tempList)-1] Works
The full slice expression a[low:high:max] sets the new slice’s cap to max - low. In your case, this means the truncated slice has a cap equal to its len (since max = len(tempList)-1 and low=0).
When you append to a slice where len == cap, Go will allocate a new underlying array instead of modifying the existing one. This breaks the shared reference chain:
- The slice you stored in
resultkeeps pointing to the original array. - Any future appends to
tempListuse a new array, so they don’t affect the already-stored slices inresult.
Second Implementation: Using copy to Avoid Shared References
Your second approach sidesteps the problem entirely by creating a copy of the current result slice every time you add it to results:
newR := make([]int, len(result)) copy(newR, result) results = append(results, newR)
Here, newR is a brand-new slice with its own underlying array, copied directly from result. Even if you modify the original result slice later (via append or truncate), the copy in results remains untouched. This is a more explicit way to prevent unintended side effects from shared slice references.
Key Takeaways
- When storing slices in a result collection, always be mindful of whether you’re storing a reference or a copy.
- The full slice expression (
a[low:high:max]) lets you control a slice’s capacity, which can prevent accidental modifications to shared underlying arrays. - Using
copyto create duplicate slices is a straightforward way to ensure your result entries stay immutable once added.
内容的提问来源于stack exchange,提问作者jack-nie

