11

I tried looking for an algorithm that would do what std::inplace_merge followed by std::unique would do. Seems more efficient to do it in 1 pass than in 2. Could not find it in standard library or by oogling.

  1. So is there implementation somewhere in boost under different name maybe?
  2. Is such algorithn possible (in a sense that it has same complexity guarantees as normal inplace_merge)?
Kijewski
  • 25,517
  • 12
  • 101
  • 143
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • Such an algorithm would be easy to implement. You could easily drop elements in the merge step of merge sort. – Kijewski Dec 06 '14 at 16:33
  • A somewhat hacky way to emulate this with boost would be a combination of the Boost.Range function `boost::inplace_merge` and the `uniqued` adapter. This would delay `unique` to the point were the range is iterated and would not add an additional N steps. Still far from perfect though. – pmr Dec 06 '14 at 16:36
  • pmr, are you sure about this ? Feels to me like range would first be inplace_merged, then uniqued ? Since compiler/boost cant know that those 2 can be fused – NoSenseEtAl Dec 06 '14 at 16:41
  • @NoSenseEtAl Yes, adapters are lazy. – pmr Dec 06 '14 at 16:46
  • @Kay look at impl of std inplace_merge and then tell me it is easy :D – NoSenseEtAl Dec 06 '14 at 17:25
  • 1
    @NoSenseEtAl: It *is* easy if you have scratch space equal to the size of the range being inserted (or the original list, if that's smaller). – Ben Voigt Dec 06 '14 at 17:34
  • @NoSenseEtAl: I don't think `inplace_merge` is *actually* in-place either. (Check your STL source code.) – user541686 Dec 09 '14 at 10:20
  • The reason why it doesn't exist is that the implementation effort of creating every remotely useful fusion of the basic standard algorithms is a LOT of work. – Sebastian Redl Dec 09 '14 at 10:21
  • Since unique changes the number of items in the range, and so makes items at the end of your container move, I'm not sure "inplace" wouldn't be a little misleading here... – Andy Newman Dec 09 '14 at 10:22

2 Answers2

4

It doesn't operate in-place, but assuming that neither range contains duplicates beforehand, std::set_union will find the same result as merge followed by unique.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
4

There are many interesting algorithms missing from the algorithms section. The original submission of STL was incomplete from Stepanov's view and some algorithms were even removed. The proposal by Alexander Stepanov and Meng Lee doesn't seem to include an algorithm inplace_merge_unique() or any variation thereof.

One of the potential reasons why there is no such algorithm is that it isn't clear which of the element should be dropped: since the comparison is only a strict weak ordering, the choice of element matters. One approach to implement inplace_merge_unique() is to

  1. Use std::remove_if() to remove any element which is a duplicate from the second range.
  2. Use inplace_merge() to do the actual merge.

The predicate to std::remove_if() would track the current position in the first part of the sequence to be merged. The code below isn't tested but something like that should work:

template <typename BiDirIt, typename Comp>
BiDirIt inplace_merge_unique(BiDirIt begin, BiDirIt middle, BiDirIt end, Comp comp) {
    using reference = typename std::iterator_traits<BiDirIt>::reference;
    BiDirIt result = std::remove_if(middle, end, [=](reference other) mutable -> bool {
            begin = std::find_if(begin, middle, [=](reference arg)->bool {
                    return !comp(arg, other);
                });
            return begin != middle && !comp(other, *begin);
        });
    std::inplace_merge(begin, middle, result, comp);
    return result;
}
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380