为 forward_list 实现 stable_partition

Posted

技术标签:

【中文标题】为 forward_list 实现 stable_partition【英文标题】:Implementing stable_partition for forward_list 【发布时间】:2018-01-03 10:49:48 【问题描述】:

我想实现类似于 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 现在会遍历错误的列表。

【问题讨论】:

我认为您应该只发布您的代码,以便我们可以看到您在做什么。 您有问题吗? @UKMonkey 添加了原始代码。 @manni66 我试图澄清一下,谢谢! 【参考方案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> l0,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.

【讨论】:

以上是关于为 forward_list 实现 stable_partition的主要内容,如果未能解决你的问题,请参考以下文章

我如何有效地从 forward_list 中删除_if 只有一个元素?

forward_list::remove 抛出编译错误

STL之序列式容器list与forward_list

QLinkedList和std::forward_list(都是双向链表,不支持operator[],好处可能是插入和删除都比较快)

SGI slist 和 C++11 forward_list 有啥区别?

C++ STL应用与实现7: 如何使用std::forward_list 单链表 (since C++11)