There are cases where loops are much faster than other alternatives. This can sometimes be difficult to predict ahead of time, however, when a loop is not a good idea is often easier to identify. In general:
1. Growing an array in a loop will always be your slowest option. The case you show above where a starts out empty and grows in a loop is going to be the slowest option.
2. The more complicated the indexing maneuvers in a loop, the slower it will be. Simple looping over consecutive elements in existing arrays will be fast.
3. A loop which makes many M-file function calls will be slow compared to a vectorized code. For speed, look for only a minimum of built-in calls within a loop.
4. These guidelines stand out more as the size of the arrays involved grows. For example, not pre-allocating a 1-by-5 array is no big deal, but a 1-by-5e7 array will slow down the code.
In your three examples above, the first example is a good demonstration of how not to pre-allocate a sparse array, as you found out. Your second example generates the ultimate sparse array - 0% density. I don't know enough about sparse arrays to know if this actually pre-allocates any memory at all. The third example is bad whether dealing with sparse arrays or not because you are growing and array in a loop.