Can't be done in O(n). The algorithm you mention is very simple to implement for two sorted lists (to get another sorted list), since insertion to the result list is O(1), so one traversal of the inputs in O(n) (single pass). For something like a TreeSet, the tree itself has to be built, that means O(log(n)) insertion time. So the best you can conceptually get is O(N log(N)).
As for it being implemented, Its very likely ++ method does exactly this, since its optimal and a well-known solution.
If you want to do this, is fairly simple, here is a small snippet of code I use to merge an arbitrary set of sorted files:
case class Input(i: Iterator[String], var lastKey: Option[K], var lastValue: String, extraction: String => (K, String), var count: Int =0)
var sources = List[Input]()
... and later...
// Read a first value for all sources
sources.foreach(readKey(_))
var moreInput = true
while(moreInput)
{
// Find the source with the smallest key
var smallest: Input = null
for (source <- sources)
if (source.lastKey.isDefined)
if (smallest == null || smallest.lastKey.get > source.lastKey.get)
smallest = source
// Check if there is anything to be done.
if (smallest == null)
moreInput = false
else
{
// Output the chosen line and advance the corresponding iterator.
out.println(smallest.lastValue)
readKey(smallest)
val index = sources.indexOf(smallest)
}
}