If you don't want gaps, you should not use sequences or IDENTITY. They are designed to be able to produce gaps. If you want a contiguous series of ids, you need to roll your own:
BEGIN TRANSACTION
SELECT @id = isnull(MAX(id), 0) FROM tbl WITH (UPDLOCK)
INSERT tbl(id, col1, col2, ...
SELECT @id, @par1, @par2, ---
...
But this also means that if there are parallel inserts they will be serialised. Not a big deal if inserts are occasional, but if there can be several per second, this pattern is not good. That's when you use a sequence or IDENTITY. And once you have decided to do that, it's kind of futile to care about gaps.
Besides gaps due to rollbacks, you can also get rollbacks due to that the sequence value is cached in memory and it is only updated on disk occasionally. This means that if the database is shut down uncleanly, the value on disk will not be updated to a lower value, but there will be a gap next time the database is active. The same is true for IDENTITY.