【问题标题】:Insertion sort of a map地图的插入排序
【发布时间】:2011-11-20 11:29:07
【问题描述】:

我有一个映射(例如,从字符到整数)。我将值一一插入到这张地图中。例如,这里有四个插入:

1: A -> 1
2: B -> 2
3: C -> 3
4: D -> 1

我想根据关联的值对映射键进行排序。所以,在每次插入时,我都会得到排序后的输出:

1: A(1)
2: B(2), A(1)
3: C(3), B(2), A(1)
4: C(3), B(2), A(1), D(1)

此外,我希望能够覆盖现有映射以保持键(字符)的唯一性。所以第五次插入:

5: A -> 27

将导致排序后的输出为:

5: A(27), C(3), B(2), D(1)

我可以做到这一点的一种方法是使用多图。多重映射的键是整数,值是字符。每次插入多张地图时首先需要检查该字符是否已经存在于多张地图中,并在执行插入之前删除该映射。多重映射使键保持有序,从而负责排序。

有没有更快的方法来做到这一点?我应该使用其他更高效的容器吗?

编辑

这是我正在使用的C++ STL multimap。它很方便,因为它保留了它的元素internally ordered

这是related question。我试图避免执行已接受的解决方案中的建议:创建另一张地图。

【问题讨论】:

    标签: c++ algorithm


    【解决方案1】:

    我会做这样的事情。想象一下您的地图将x 映射到y。我会创建一个结构(类,随便):

    struct element
    {
        map_this_t x;
        to_this_t  y;
        bool operator < (element &rhs)
        {
            return y < rhs.y;       // sorted based on y
        }
    };
    

    然后创建一个set&lt;element&gt; 并插入其中。如果您遍历集合中的所有数据,您可以按照您想要的顺序获得数据。插入时,先搜索一下是否存在你想要的x值的元素。如果是这样,只需更新y,否则插入一个新元素。

    【讨论】:

    • 您将如何执行“搜索并查看”步骤?该集合基于y 排序,您正在寻找x 的一些特定值。我可以使用地图来解决这个问题,但它最终比我原来的多地图方法要慢。此外,不可能像 const 那样简单地更新 y - 更改它会使集合中元素的位置无效。删除和重新插入似乎是必要的,这会花费额外的时间。
    • 你是对的。更新它意味着删除并重新插入,每个都需要O(log n)。要搜索,您可以线性搜索 (O(n)),或者在将 x 映射到 y 的一侧有一个映射,然后您可以识别与 x 对应的 y 并删除 (x , y) 从集合中插入 (x, newy) (每个步骤都是O(log n)
    【解决方案2】:

    一般来说,地图是一个经过洗牌的数据结构(至少对于关联值而言)。 因此,对地图的元素进行排序是没有意义的。

    因此,您应该使用列表或数组之类的东西来保持元素排序。

    我认为解决您的问题的最佳方法是最简单的方法:将元素存储在列表中并对其进行排序。或者你可以使用堆。

    更新:

    地图是一种关联容器,用于存储形成的元素 通过键值和映射值的组合。

    在地图中,键值一般用于唯一标识 元素,而映射的值是与 这把钥匙。键和映射值的类型可能不同。例如,一个 地图的典型示例是电话指南,其中名称是 键和电话号码是映射的值。

    内部,map中的元素从低到高排序 遵循特定的严格弱排序标准设置的键值 建设。

    作为关联容器,它们专门设计为 通过键有效地访问其元素(与序列不同) 容器,它们通过其更有效地访问元素 相对或绝对位置)[1]。

    [1] http://www.cplusplus.com/reference/stl/map/

    【讨论】:

    • 这是一些令人困惑的解释。地图通常是有序而不是打乱的。对元素进行排序不是没有意义的,而是多余的。
    • @Masoud M。在一般情况下,您可能是对的。但是,C++ STL 多图是内部排序的。我已经编辑了我的问题以使其更加明显。列表解决方案效率太低,因为我必须在每次插入后进行排序,并且没有有效的方法来保持键的唯一性。
    • 当我们实现一个映射yes时,我们应该存储有序的键。但它对地图用户是透明的。
    • @misha:添加了一些特定提示以在插入时保持列表有序
    • @misha,每次插入一个list,你不需要使用它,你遍历列表并将它插入到正确的位置,所以它实际上是O(n)
    【解决方案3】:

    根据定义,地图按其键排序。

    这些值不能用于映射的排序谓词,因为这些值是非常量的!更改它们会使容器不变量(排序)无效。

    如果您想要按“值”排序的条目,它隐含地是一个键,您可能需要一个

    std::set<std::pair<key, value> >
    

    更新这是一个工作演示:https://ideone.com/SgbEN

    相反。为了能够通过不同的谓词重新排序,您需要一个列表、向量或任何其他顺序容器。

    编辑

    列表解决方案效率太低,因为我必须在每次插入后进行排序,并且没有有效的方法来保持键的唯一性。

    这里的解决方案是使用

    • std::make_heap
    • std::push_heap
    • std::sort_heap

    算法。它们适用于列表(以及用于廉价 value_types 的向量 - 或使用 c++0x 移动语义)

    如果您在按顺序使用列表之前无法负担 sort_heap 步骤:使用

    insert_point = std::lower_bound(lst.begin(), lst.end(), insertee);
    st.insert(insertion_point, insertee);
    

    直接插入到有序位置。您可以期望 push_heap 对于许多插入更快,而lowerbound/insert 对于许多访问会更快

    【讨论】:

    • @misha:寻找插入点(lower_bound)是 O(log n),因为使用了二进制搜索。所以在任何情况下都保持它的顺序不是二次的。请注意将项目保存在 Btree 中的 std::set 示例(在大多数实现中),就像 std::map
    【解决方案4】:

    只需使用map &lt;char,int&gt;,您的密钥将保持唯一,一对&lt;char,int&gt; 将按顺序存储在其中。

    要根据元素的关联值对元素进行排序,请将 pair&lt;int,char&gt; 也存储在一个集合中。

    【讨论】:

    • 他希望地图从charint,但是数据按照int排序
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多