为forward_list实现stable_partition

时间:2018-01-03 10:49:48

标签: c++ stl singly-linked-list

我想实现类似于std :: stable_partition的东西,但是对于c ++ 11的forward_list。 stl版本需要双向迭代器,但是通过利用容器特定的方法,我相信我可以有效地得到相同的结果。

示例声明:

template <typename T, typename UnaryPredicate>
void stable_partition(std::forward_list<T>& list, UnaryPredicate p);

(虽然可以添加开始和结束迭代器,但为了简洁我省略了它们。返回分区点也一样)

我已经计算出算法在我自己的列表类型上完成了这个,但我在stl中实现它有麻烦。

关键方法似乎是splice_after。其他方法需要内存分配和复制元素。

算法草图:

  • 创建一个新的空列表。它将保持所有元素p返回true。
  • 循环遍历目标列表,根据调用p将项添加到真实列表。
  • 将真实列表连接到目标列表的开头。

通过适当的编码,这应该是线性时间(循环内的所有操作都可以在恒定时间内完成)并且无需额外的内存分配或复制。

我正在尝试使用splice_after来实现第二步,但我最终要么找到错误的元素,要么使我的迭代器失效。

问题:

splice_after的正确用法是什么,以便我避免使用 在列表之间混合迭代器并插入正确的元素?

第一次尝试(我希望它如何运作):

template <typename T, typename UnaryPredicate>
void stable_partition(std::forward_list<T>& list, UnaryPredicate p)
{
    std::forward_list<T> positives;
    auto positives_iter = positives.before_begin();

    for (auto iter = list.begin(); iter != list.end(); ++iter)
    {
        if (p(*iter))
            positives.splice_after(positives_iter, list, iter);
    }

    list.splice_after(list.before_begin(), positives);
}

不幸的是,这至少有一个主要缺陷:splice_after在iter之后插入,并且插入了错误的元素。

此外,当元素移动到另一个列表时,递增iter现在遍历错误的列表。

1 个答案:

答案 0 :(得分:1)

必须维护std::forward_list::splice_after的前面的迭代器,这有点棘手,但仍然很短:

template<class T, class UnaryPredicate>
std::array<std::forward_list<T>, 2>
stable_partition(std::forward_list<T>& list, UnaryPredicate p) {
    std::array<std::forward_list<T>, 2>  r;
    decltype(r[0].before_begin()) pos[2] = {r[0].before_begin(), r[1].before_begin()};
    for(auto i = list.before_begin(), ni = i, e = list.end(); ++ni != e; ni = i) {
        bool idx = p(*ni);
        auto& p = pos[idx];
        r[idx].splice_after(p, list, i);
        ++p;
    }
    return r;
}

用法示例:

template<class T>
void print(std::forward_list<T> const& list) {
    for(auto const& e : list)
        std::cout << e << ' ';
    std::cout << '\n';
}

int main() {
    std::forward_list<int> l{0,1,2,3,4,5,6};
    print(l);
    // Partition into even and odd elements.
    auto p = stable_partition(l, [](auto e) { return e % 2; });
    print(p[0]); // Even elements.
    print(p[1]); // Odd elements.
}