【问题标题】:Swap neighbouring elements in std::list交换 std::list 中的相邻元素
【发布时间】:2014-02-14 11:18:08
【问题描述】:

我想改变std::list中相邻元素的位置

列表和值的示例

A B C D E F G
3 2 1 2 1 3 2

排序后我期望收到的:

A B D C F E G
3 2 2 1 3 1 2

所以,简单的A > B = 无事可做,但C < D = 交换它们并转到E 比较。

我不知道如何交换相邻元素。

所以,我想前进 1 步 good 元素

【问题讨论】:

  • splice()std::swap(*itr1, *itr2)erase()/insert()
  • @abrahab: std::vector,将元素存储在连续的内存块中会更好
  • 你应该看看std::iter_swap
  • @LihO std::list::sort 完全能够有效地对列表进行排序。
  • @LihO OP 不想对列表进行排序。如果某些条件成立,他想遍历列表并交换相邻元素。这在std::list 上工作得很好。

标签: c++ sorting stl containers


【解决方案1】:

您可以使用两个迭代器轻松做到这一点:

void biswap(std::list<int> &l)
{
    if (l.size() < 2)
        return;
    auto it2 = l.begin();
    auto it1 = it2++;
    auto e = l.end();
    for (;;)
    {
        if (*it1 < *it2)
            std::swap(*it1, *it2);
        it1 = it2++;
        if (it2 == e)
            return;
        it1 = it2++;
        if (it2 == e)
            return;
    }
}

Live example

注意:如果您不使用 C++11 并因此调用 size() 可能会产生很大的开销,您可以用它替换它(当然,用显式类型替换 auto 的所有用法):

void biswap(std::list<int> &l)
{
    auto it2 = l.begin();
    auto e = l.end();
    if (it2 == e)
        return;
    auto it1 = it2++;
    if (it2 == e)
        return;
    for (;;)
    // ... the rest as before
}

【讨论】:

  • 谢谢,很好,它可以完成我想要的任务,但似乎需要注意列表中的.size() 在执行时间上可能是线性的(大列表的性能问题),也许更好地测试列表的其他东西开始?还要感谢您在现场示例中提供的出色测试平台。
  • @abrahab 在 C++11(我在代码中使用过)中,std::list::size() 保证是恒定时间。当然,用两个 end() 测试替换它是微不足道的 - 我会编辑它。
【解决方案2】:

您可以使用标准算法std::adjacent_find 找到第一个

例如

#include <iostream>
#include <algorithm>
#include <list>
#include <functional>
#include <iterator>

int main()
{
   std::list<int> l = { 3, 2, 1, 2, 1, 3, 2 };

   for ( int x : l ) std::cout << x << ' ';
   std::cout << std::endl;

   auto it = std::adjacent_find( l.begin(), l.end(), std::less<int>() );

   if ( it != l.end() ) std::swap( *it, *std::next( it ) );

   for ( int x : l ) std::cout << x << ' ';
   std::cout << std::endl;
} 

输出是

3 2 1 2 1 3 2 
3 2 2 1 1 3 2 

或者如果你想在第一个

#include <iostream>
#include <algorithm>
#include <list>
#include <functional>
#include <iterator>

int main()
{
    std::list<int> l = { 3, 2, 1, 2, 1, 3, 2 };

    for ( int x : l ) std::cout << x << ' ';
    std::cout << std::endl;

    std::list<int>::iterator it = l.begin();

    while ( ( it = std::adjacent_find( it, l.end(), std::less<int>() ) ) != l.end() )
    {
        std::swap( *it, *std::next( it ) );
    }

    for ( int x : l ) std::cout << x << ' ';
    std::cout << std::endl;
} 

输出是

3 2 1 2 1 3 2 
3 2 2 1 3 2 1

【讨论】:

  • 这也将比较 BC,OP 显然不想这样做。
  • @Angew 我不确定他是否只想比较 C 和 D。我想他想订购列表。
  • 嗯,您的输出与 Q 中所述的 OP 所需的输出不匹配。
  • @Angew 不清楚他想要什么,因为他已经更新了他的帖子。尽管如此,我还是展示了一些他可以使用的方法。
  • 感谢该方法的建议,它可能很有用,但经过一些测试,我发现它不适合我自己的任务。 @Angew 是对的。您的解决方案订购了整个列表,它可以将一些元素从原始位置移动超过 1 步。 std::list&lt;int&gt; l = { 2, 4, 2, 3, 1, 2, _1_, 3, 2, 3, 4 }; 在这一行上带下划线的 1 移到列表的末尾,(距离原始的 4 个步骤),但感谢您的解决方案。
【解决方案3】:

这给出了所需的结果,并完全按照您的要求切换了“好”元素的比较:

#include <list>
#include <iostream>

using namespace std;

template<typename Type>
void print(const list<Type> & l)
{
    for (auto element : l) cout << element << " ";
    cout << endl;
}

int main(int argc, const char *argv[])
{
    list<int> l = {3,2,1,2,1,3,2}; 

    print(l);

    auto it = l.begin(); 
    while(std::next(it) != l.end())
    {
        if (*it < *std::next(it))
        {
            std::swap(*it, *std::next(it)); 
            ++it; 
        }
        ++it; 
    } 

    print(l); 

    return 0;
}

执行结果:

3 2 1 2 1 3 2 
3 2 2 1 3 1 2

【讨论】:

  • 非常好的解决方案,它也适合我的任务。似乎它也是“如果没有交换,则交换下一个元素”很好!
【解决方案4】:

如果您没有 C++11 支持,使用 2 个迭代器的简单 C++03 解决方案可能是:

#include <iostream>
#include <algorithm>
#include <list>

void swapNeighbours(std::list<int>& l) {

    std::list<int>::iterator it = l.begin();
    std::list<int>::iterator prev = it++;
    while (prev != l.end() && it != l.end()) {

        // swap if needed:
        if (*prev < *it)
            std::swap(*prev, *it);

        // move 2 times forward:
        if (++it == l.end())
            break;
        prev = it++;
    }
}

然后(根据您的示例)如果您这样做:

void print(const std::list<int>& l) {
    std::list<int>::const_iterator i;
    for (i = l.begin(); i != l.end(); ++i) {
        std::cout << *i << ' ';
    }
    std::cout << std::endl;
}

int main() {

    std::list<int> l;
    l.push_back(3); l.push_back(2); l.push_back(1); l.push_back(2);
    l.push_back(1); l.push_back(3); l.push_back(2);

    print(l);
    swapNeighbours(l);
    print(l);
}

那么对于:

A B C D E F G
3 2 1 2 1 3 2

会有以下比较:A &lt; B? (否)C &lt; D? (是的,交换)E &lt; F? (是的,交换也是)产生输出:

3 2 2 1 3 1 2 

【讨论】:

    【解决方案5】:

    如果你想要做的只是成对遍历和交换元素,如果第一个小于第二个,那么你可以例如像这样使用std::adjacent_find

    using std::swap;
    for (auto it = std::begin(l); it != std::end(l); it = std::adjacent_find(it, std::end(l), std::less<int>{})) {
        swap(*it, *std::next(it));
    }
    

    这将导致数字列表3 2 1 2 1 3 2 排列为:

    3 2 2 1 3 2 1
    

    但是,这与您的预期结果3 2 2 1 3 1 2 相同,我不明白您为什么不想交换最后一对1 2和其他人一样吗?

    如果你想要的是一个 不稳定的 排序模式,那么就没有简单的解决方案。相反,您必须对单个元素进行特殊处理。

    【讨论】:

    • 也许您的解决方案也有用(需要在真实应用程序上进行更多测试),但在最初的想法中,我不想交换最后一对,因为这对中的一个元素已经交换.因此,如果我们将其交换 - 元素 E 将改变位置 2 次。所以,从原来的位置走两步可能对我的任务来说太重要了。
    猜你喜欢
    • 1970-01-01
    • 2021-05-04
    • 1970-01-01
    • 1970-01-01
    • 2019-11-27
    • 2018-02-24
    • 2021-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多