排序元素,但保持某些元素固定

时间:2015-08-15 02:01:26

标签: c++ sorting templates lambda

功能

template <typename Container, typename Comparator, typename Predicate>
void sortButKeepSomeFixed (Container& c, const Comparator& comp, const Predicate& pred)

是根据排序标准c对容器comp进行排序,但满足pred的那些元素在排序后应保持固定在原始位置(即不受排序影响)。

我试图采用快速排序来适应这种情况,但却无法想到它。最后,我决定调整原油选择排序以完成工作:

#include <iostream>
#include <vector>

std::vector<int> numbers = {5,7,1,8,9,3,20,2,11};

template <typename Container, typename Comparator, typename Predicate>
void sortButKeepSomeFixed (Container& c, const Comparator& comp, const Predicate& pred) {  // O(n^2), but want O(nlogn) on average (like quick sort or merge sort)
    const std::size_t N = c.size();
    std::size_t i, j, minIndex;
    for (i = 0; i < N-1; i++) {
        if (pred(c[i]))
            continue;  // c[i] shall not swap with any element.
        minIndex = i;
        for (j = i + 1; j < N; j++) {
            if (pred(c[j]))
                continue;  // c[j] shall not swap with any element.
            if (comp(c[j], c[minIndex]))
                minIndex = j;
        }
        if (minIndex != i)
            std::swap(c[i], c[minIndex]);
    }
}

int main() {
    sortButKeepSomeFixed (numbers,
        std::greater<int>(),  // Ordering condition.
        [](int x) {return x % 2 == 0;});  // Those that shall remain fixed.
    for (int x : numbers) std::cout << x << ' ';  // 11 9 7 8 5 3 20 2 1
}

但时间复杂度是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];
}

错误输出为11 9 9 8 11 3 20 2 9时应为11 9 7 8 5 3 20 2 1

1 个答案:

答案 0 :(得分:0)

基于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.
}

设置和映射的错误是&#34;分配只读位置&#34;。 任何人都知道如何概括它以使用集合和地图?

更新:所以我建议使用set,maps等...,只需删除那些满足pred的元素,并创建一个新的set / map / ...并将Compare作为key_compare类型。如下。但这只是为了设定。如何将其推广到具有key_compare类型的其他容器?

template <typename Container, typename Comparator, typename Predicate>
std::set<typename Container::value_type, Comparator, typename Container::allocator_type>
        sortButRemoveSomeElements (Container& c, const Comparator&, const Predicate& pred) {
    std::set<typename Container::value_type, Comparator, typename Container::allocator_type> set;
    std::vector<typename Container::value_type> keep;
    for (typename Container::iterator it = c.begin();  it != c.end();  ++it) {
        if (!pred(*it))
            keep.emplace_back(*it);
    }
    for (typename Container::value_type x : keep)
        set.emplace(x);  // Sorted by Comparator automatically due to std::set's insertion property.
    return set;
}

测试:

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