, , ( ).
template <typename Container>
void
removeSmallestNonunique(Container& c)
{
using value_type = typename Container::value_type;
if (c.size() > 1)
{
std::make_heap(c.begin(), c.end(), std::greater<value_type>{});
std::pop_heap(c.begin(), c.end(), std::greater<value_type>{});
for (auto e = std::prev(c.end()); e != c.begin(); --e)
{
std::pop_heap(c.begin(), e, std::greater<value_type>{});
if (*e == e[-1])
{
c.erase(e);
break;
}
}
}
}
, . , , sort/adjacent_find . .
, , , sort/adjacent_find. , , , , , , , sort/adjacent_find.
, , . .
, Omid .: -)
7 ...
Omid's, , , , .
, , clang/lib++ -O3:
#include <unordered_map>
#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
#include <chrono>
#include <cassert>
template <typename Container>
void
erase_using_hashTable(Container& vec)
{
using T = typename Container::value_type;
std::unordered_map<T, int> c;
for (const auto& elem : vec){
++c[elem];
}
bool has = false;
T min_;
for (const auto& e : c)
{
if (e.second > 1)
{
min_ = has ? std::min(e.first, min_) : e.first;
has = true;
}
}
if (has)
vec.erase(std::find(vec.begin(), vec.end(), min_));
}
template <typename Container>
void
eraseSmallestNonunique(Container& c)
{
std::sort(std::begin(c), std::end(c));
auto it = std::adjacent_find(std::begin(c), std::end(c));
if (it != std::end(c))
c.erase(it);
}
template <typename Container>
void
removeSmallestNonunique(Container& c)
{
using value_type = typename Container::value_type;
if (c.size() > 1)
{
std::make_heap(c.begin(), c.end(), std::greater<value_type>{});
std::pop_heap(c.begin(), c.end(), std::greater<value_type>{});
for (auto e = std::prev(c.end()); e != c.begin(); --e)
{
std::pop_heap(c.begin(), e, std::greater<value_type>{});
if (*e == e[-1])
{
c.erase(e);
break;
}
}
}
}
template<typename iterator>
iterator partition_and_find_smallest_duplicate(iterator begin, iterator end)
{
using std::swap;
if (begin == end)
return end;
iterator pivot = begin;
iterator first = next(begin);
iterator last = end;
while (first != last) {
if (*first > *pivot) {
--last;
swap(*first, *last);
} else if (*first < *pivot) {
++first;
} else {
++pivot;
swap(*pivot, *first);
++first;
}
}
auto res = partition_and_find_smallest_duplicate(next(pivot), first);
if (res != first)
return res;
if (pivot != begin)
return pivot;
return partition_and_find_smallest_duplicate(last, end);
}
template<typename container>
void remove_smallest_duplicate(container& c)
{
using std::swap;
auto it = partition_and_find_smallest_duplicate(c.begin(), c.end());
if (it != c.end())
{
swap(*it, c.back());
c.pop_back();
}
}
int main()
{
const int MaxArraySize = 5000000;
const int minArraySize = 5;
const int numberOfTests = 3;
std::mt19937 generator;
for (int t = minArraySize; t <= MaxArraySize; t *= 10)
{
const int range = 3*t/2;
std::uniform_int_distribution<int> distribution(0,range);
std::cout << "Array size = " << t << " range = " << range << '\n';
std::chrono::duration<double> avg{},avg2{}, avg3{}, avg4{};
for (int n = 0; n < numberOfTests; n++)
{
std::vector<int> save_vec;
save_vec.reserve(t);
for (int i = 0; i < t; i++){
save_vec.push_back(distribution(generator));
}
auto vec = save_vec;
auto start = std::chrono::steady_clock::now();
erase_using_hashTable(vec);
auto end = std::chrono::steady_clock::now();
avg += end - start;
auto answer1 = vec;
std::sort(answer1.begin(), answer1.end());
vec = save_vec;
start = std::chrono::steady_clock::now();
eraseSmallestNonunique(vec);
end = std::chrono::steady_clock::now();
avg2 += end - start;
auto answer2 = vec;
std::sort(answer2.begin(), answer2.end());
assert(answer2 == answer1);
vec = save_vec;
start = std::chrono::steady_clock::now();
removeSmallestNonunique(vec);
end = std::chrono::steady_clock::now();
avg3 += end - start;
auto answer3 = vec;
std::sort(answer3.begin(), answer3.end());
assert(answer3 == answer2);
vec = save_vec;
start = std::chrono::steady_clock::now();
remove_smallest_duplicate(vec);
end = std::chrono::steady_clock::now();
avg4 += end - start;
auto answer4 = vec;
std::sort(answer4.begin(), answer4.end());
assert(answer4 == answer3);
}
std::cout << "Method1 : " << (avg / numberOfTests).count() << 's'
<< "\nMethod2 : " << (avg2 / numberOfTests).count() << 's'
<< "\nMethod3 : " << (avg3 / numberOfTests).count() << 's'
<< "\nMethod4 : " << (avg4 / numberOfTests).count() << 's'
<< "\n\n";
}
}
:
Array size = 5 range = 7
Method1 : 8.61967e-06s
Method2 : 1.49667e-07s
Method3 : 2.69e-07s
Method4 : 2.47667e-07s
Array size = 50 range = 75
Method1 : 2.0749e-05s
Method2 : 1.404e-06s
Method3 : 9.23e-07s
Method4 : 8.37e-07s
Array size = 500 range = 750
Method1 : 0.000163868s
Method2 : 1.6899e-05s
Method3 : 4.39767e-06s
Method4 : 3.78733e-06s
Array size = 5000 range = 7500
Method1 : 0.00124788s
Method2 : 0.000258637s
Method3 : 3.32683e-05s
Method4 : 4.70797e-05s
Array size = 50000 range = 75000
Method1 : 0.0131954s
Method2 : 0.00344415s
Method3 : 0.000346838s
Method4 : 0.000183092s
Array size = 500000 range = 750000
Method1 : 0.25375s
Method2 : 0.0400779s
Method3 : 0.00331022s
Method4 : 0.00343761s
Array size = 5000000 range = 7500000
Method1 : 3.82532s
Method2 : 0.466848s
Method3 : 0.0426554s
Method4 : 0.0278986s
. . !
, " O (N ^ 2)", . , , , , :
O (N ^ 2), , / (, 3 2). 4 , . 3 2 , , 4.
O (N ^ 2) - , . 4 .