Deduplication generally maintains the structure of the original data (order, hierarchy, etc), while removing the duplicates and replacing them with lightweight references, thus saving space. If you dedup some data, you wouldn't notice any structural change, only (possible) performance gain.
Debounce simply removes the repeated consecutive elements (rather, merging bouncing data when appropriate, but I think this is the closest term here).
If you have ["foo", "foo", "bar", "foo", "hello"], deduplication would, in a strict sense, result in Reference [0, 0, 1, 0, 2] + Storage ["foo", "bar", "hello"] however it looks like in the end, which doesn't happen here.
I'm not reducing them to zero instances. a should still be [1, 2, 3], you can even try [1, 1, 2, 2, 3, 3, 3] which yields the same result. The sample code shows that range expression is incorrect. start points to 1 and end points to 3. So start..<end should start from 1 up to but excluding 3.
original // [1, 1, 2, 2, 2, 3, 3, 3, 3, 3]
a // [1, 2, 3]
start // ^ ^
end // |
a[start..<end] // [1, 2) exclude the end element.
And the above would be the result if you do it by hand.
You seem to have already realised this, so I don't know where the confusion is.
You're not caching anything. It's the same amount of computation whether you store upperBound or not.
- You cache
upperBound to get next.lowerBound quickly,
but still use next.lowerBound to slowly compute next.upperBound
- You only use
lowerBound to slowly compute next.lowerBound.
And you're not using upperBound outside the context of advancing either.