C++ 对元素进行排序,但保持某些元素不变

C++ 对元素进行排序,但保持某些元素不变,c++,sorting,templates,lambda,C++,Sorting,Templates,Lambda,功能 template <typename Container, typename Comparator, typename Predicate> void sortButKeepSomeFixed (Container& c, const Comparator& comp, const Predicate& pred) 但是时间复杂度是O(N^2)(我认为)。有人能将这里的时间复杂度平均提高到O(NlogN)吗?换句话说,使用递归或类似的方法,找到一个总体

功能

template <typename Container, typename Comparator, typename Predicate>
void sortButKeepSomeFixed (Container& c, const Comparator& comp, const Predicate& pred)
但是时间复杂度是O(N^2)(我认为)。有人能将这里的时间复杂度平均提高到O(NlogN)吗?换句话说,使用递归或类似的方法,找到一个总体上更好的算法

或者更好的方法是取出满足
pred
的元素,对剩下的
std::sort
进行排序,然后将提取的元素放回原始位置?这会更有效率吗,还是会让事情变得更糟

更新: 这是基于Beta的建议(对未通过pred的迭代器进行排序)。但是,尽管通过pred的元素确实保持不变,但最后的排序是不正确的

template <typename Container, typename Comparator, typename Predicate>
void sortButKeepSomeFixed (Container& c, const Comparator& comp, const Predicate& pred) {
    std::vector<typename Container::iterator> iterators;
    for (typename Container::iterator it = c.begin();  it != c.end();  ++it) {
        if (!pred(*it))
            iterators.emplace_back(it);
    }
    std::vector<typename Container::iterator> originalIterators = iterators;
    std::sort(iterators.begin(), iterators.end(),
        [comp](const typename Container::iterator& x, const typename Container::iterator& y)
        {return comp(*x, *y);});
    for (int i = 0; i < originalIterators.size(); i++)
        *originalIterators[i] = *iterators[i];
}
模板
void sortButKeepSomeFixed(容器和c、常量比较器和comp、常量谓词和pred){
向量迭代器;
对于(typename容器::迭代器it=c.begin();it!=c.end();++it){
如果(!pred(*it))
迭代器。向后放置(it);
}
std::vector originalIterators=迭代器;
std::sort(迭代器.begin(),迭代器.end(),
[comp](常量typename容器::迭代器&x,常量typename容器::迭代器&y)
{返回comp(*x,*y);});
对于(int i=0;i

不正确的输出是
11 9 9 8 11 3 20 2 9
,而它应该是
11 9 7 8 5 3 20 2 1
,这是基于Beta使用迭代器排序的想法,尽管我不确定时间复杂度是多少。它也不适用于所有容器,例如std::set、std::map

template <typename Container, typename Comparator, typename Predicate>
void sortButKeepSomeFixed (Container& c, const Comparator& comp, const Predicate& pred) {
    std::vector<typename Container::value_type> toSort;
    std::vector<typename Container::iterator> iterators;
    for (typename Container::iterator it = c.begin();  it != c.end();  ++it) {
        if (!pred(*it)) {
            toSort.emplace_back(*it);
            iterators.emplace_back(it);
        }
    }
    std::sort(toSort.begin(), toSort.end(), comp);
    for (std::size_t i = 0; i < toSort.size(); i++)
        *iterators[i] = toSort[i];
}

std::vector<int> vector   = {5,7,1,8,9,3,20,2,11};
std::array<int, 9> array = {5,7,1,8,9,3,20,2,11};
std::list<int> list       = {5,7,1,8,9,3,20,2,11};
std::set<int> set         = {5,7,1,8,9,3,20,2,11};
std::map<double, int> map = { {1.5,5}, {1.2,7}, {3.5,1}, {0.5,8}, {5.2,9}, {7.5,3}, {0.1,20}, {1.8,2}, {2.4,11} };

template <typename Container>
void test (Container& container) {
    sortButKeepSomeFixed (container,
        std::greater<int>(),  // Ordering condition.
        [](int x) {return x % 2 == 0;});  // Those that shall remain fixed.
    for (int x : container) std::cout << x << ' ';
    std::cout << '\n';
}

int main() {
    test(vector);  // 11 9 7 8 5 3 20 2 1
    test(array);  // 11 9 7 8 5 3 20 2 1
    test(list);  // 11 9 7 8 5 3 20 2 1
    test(set);  // Does not compile.
    sortButKeepSomeFixed (map,
        [](const std::pair<double, int>& x, const std::pair<double, int>& y) {return x.second > y.second;},
        [](const std::pair<double, int>& x) {return x.second % 2 == 0;});
    for (const std::pair<double, int>& x : map)
        std::cout << "(" << x.first << "," << x.second << ") ";  // Does not compile.
}
测试:

struct GreaterThan { bool operator()(int x, int y) const {return x > y;} };
std::set<int, GreaterThan> newSet = sortButRemoveSomeElements (set,
    GreaterThan{},  // Ordering condition.
    [](int x) {return x % 2 == 0;});  // Those that shall be removed.
for (int x : newSet) std::cout << x << ' ';  // 11 9 7 5 3 1
struct GreaterThan{bool operator()(intx,inty)const{return x>y;};
std::set newSet=sortButRemoveSomeElements(set,
大于{},//排序条件。
[](int x){返回x%2==0;});//那些应该被移除的。

对于(intx:newSet)std::cout,这是一个有趣的例子。我首先尝试编写IMO正确的方法,使用只跳过满足谓词的元素的方法。事实证明,这是一个相当具有挑战性的问题,至少在我做这件事的时候,我会在手机上写下它

基本上,这将产生类似于Eric Niebler的代码

但也有一种更简单、直接的方法,您正试图使用上面提到的方法。非工作解决方案的问题是,它正在更改(其余排序的)迭代器在最后一个
for
循环中赋值时指向的值。可以通过复制来避免此问题,如在我的代码中:

int main(int, char **) {
 vector<int> input {1,2,3,4,5,6,7,8,9};
 vector<reference_wrapper<int>> filtered{begin(input), end(input)};
 filtered.erase(remove_if(begin(filtered), end(filtered),
         [](auto e) {return e%2==0;}), end(filtered));
 vector<int> sorted{begin(filtered), end(filtered)};
 // change that to contain reference wrappers to see the issue
 sort(begin(sorted), end(sorted),
      greater<int>{});
 transform(begin(filtered), end(filtered),
    begin(sorted),
    begin(filtered),
    [](auto to, auto from) {
      to.get() = from; return to;});
 copy(begin(input), end(input),
      ostream_iterator<int>{cout, ", "});
 return 0;
}
在构造时,该类存储指向其参数的指针,在使用复制赋值运算符时也会修改该参数。但是,当一个实例被复制构造时,就会生成一个被引用(由另一个引用)值的堆分配副本。这样,就可以使用类似于的代码交换两个引用值

Value a, b;
copyable_ref<Value> ref_a{a}, ref_b{b};
copyable_ref<Value> temp{ref_a};
ref_a = ref_b;
ref_b = temp;
// a and b are swapped
最后,虽然这很有效,但我可能不会在生产代码中使用它。我特别惊讶的是,
std::sort
没有使用我自己的
swap
实现,这导致了这个冒险的复制构造函数



您不能将您的代码概括为适用于集合和映射:这些集合和映射是按设计排序的,它们需要固定的顺序才能正常工作。无序的变体是无序的,因此无法维持秩序。但是您可以(只要不修改容器)在向量中使用
std::reference\u wrapper
s来提供数据的排序“视图”。

您的“更好的想法”将非常有效,O(N logN)。O(N logN)表示
std::sort
本身,但是,删除元素然后将它们放回原始位置所花费的时间增加了很多,不是吗?如果您使用类似于
向量的东西,而不是自己处理插入/删除元素,则插入/删除会摊销
O(N)
的复杂性,所以这很好。事实上,不,对不起。如果要在适当的位置进行复制,例如,如果不创建原始阵列的副本,则需要复制周围的内容。如果一个接一个地进行删除,那么每次插入/删除都将是
O(N)
,因此总的复杂性将是O(N^2)。我建议您为那些没有通过谓词的人创建一个新数组,如果运行性能是您的优先级,那么内存复杂性是
O(N)
,而不是
O(1)
。有时应该问“为什么?”:此实现支持大多数元素不受影响的情况。其他替代方法是复制回去,重新评估
Pred
以找到需要跳过的元素(我的直觉认为这是最好的通用版本),或者将迭代器保留在需要跳过的位置(这有利于
Pred
昂贵的情况)。对映射和集进行排序确实没有意义。因为对于集合,它们已经按元素本身排序;对于映射,它们已经按键排序。这就是为什么我们使用映射和集合,总是保持它们的排序,所以每当我们需要查找/插入/删除元素时,总是
O(logN)
。好的,那么我建议对于集合、映射等,只需删除满足pred的元素并创建一个新的集合/map/。。。将Compare作为其键\u Compare类型。在这方面,我已经编写了一个适用于集合的函数,但是推广?@Daniel Jour感谢您修复了我最初的迭代器尝试。至于您的自定义迭代器尝试,您认为它可以修复吗?它看起来非常有趣,如果你认为它是可以修复的,我会尝试自己修复它(这将是“最酷”的解决方案)。据我所知,你已经在自己的答案中这样做了。这基本上就是我所做的。让我烦恼的是,我希望有必要的副本。这不应该是那样的,我正在努力解决这个问题。我知道,我还没有准备好。不幸的是,我没有叉子,所以前一个暂时丢了。@Daniel Jour你可能对这个更感兴趣
int main(int, char **) {
 vector<int> input {1,2,3,4,5,6,7,8,9};
 vector<reference_wrapper<int>> filtered{begin(input), end(input)};
 filtered.erase(remove_if(begin(filtered), end(filtered),
         [](auto e) {return e%2==0;}), end(filtered));
 vector<int> sorted{begin(filtered), end(filtered)};
 // change that to contain reference wrappers to see the issue
 sort(begin(sorted), end(sorted),
      greater<int>{});
 transform(begin(filtered), end(filtered),
    begin(sorted),
    begin(filtered),
    [](auto to, auto from) {
      to.get() = from; return to;});
 copy(begin(input), end(input),
      ostream_iterator<int>{cout, ", "});
 return 0;
}
template <class T>
class copyable_ref {
public:
  copyable_ref(T& ref) noexcept
  : _ptr(std::addressof(ref)), _copied(false) {}
  copyable_ref(T&&) = delete;
  copyable_ref(const copyable_ref& x) noexcept
  : _ptr (new int(*x._ptr)), _copied (true) {
  }
  ~copyable_ref() {
    if (_copied) {
      delete _ptr;
    }
  }
  copyable_ref& operator=(const copyable_ref& x) noexcept {
    *_ptr = *x._ptr;
  }
  operator T& () const noexcept { return *_ptr; }
  T& get() const noexcept { return *_ptr; }
private:
  T* _ptr;
  bool _copied;
};
Value a, b;
copyable_ref<Value> ref_a{a}, ref_b{b};
copyable_ref<Value> temp{ref_a};
ref_a = ref_b;
ref_b = temp;
// a and b are swapped
 vector<int> input {1,2,3,4,5,6,7,8,9};

 vector<copyable_ref<int>> sorted;
 sorted.reserve(input.size());
 for (auto & e : input) {
    if (e % 2 != 0) {
      sorted.emplace_back(e);
    }
 }
 sort(begin(sorted), end(sorted),
      greater<int>{});
 copy(begin(input), end(input),
      ostream_iterator<int>{cout, ", "});
 cout << endl;
 // 9 2 7 4 5 6 3 8 1