基于数值将stl映射转换为键的排序列表的良好算法

时间:2012-05-24 17:30:31

标签: algorithm sorting stl map c++03

我有一个类型为:

的stl地图
map<Object*, baseObject*>

其中

class baseObject{
    int ID;
    //other stuff
};

如果我想返回一个对象列表(std :: list&lt; Object *&gt;),那么按照baseObject.ID的顺序对它进行排序的最佳方法是什么?

我只是在寻找每一个数字或什么?我宁愿不将地图更改为增强地图,虽然我不一定反对做一些像

这样的返回函数中包含的内容。
GetObjectList(std::list<Object*> &objects)
{
   //sort the map into the list
}

编辑:也许我应该迭代并将obj-&gt; baseobj复制到baseobj.ID-&gt; obj的地图中?

6 个答案:

答案 0 :(得分:2)

我要做的是先将密钥(因为你只想将它们返回)解压缩到一个向量中,然后对其进行排序:

std::vector<baseObject*> out;
std::transform(myMap.begin(), myMap.end(), std::back_inserter(out), [](std::pair<Object*, baseObject*> p) { return p.first; });
std::sort(out.begin(), out.end(), [&myMap](baseObject* lhs, baseObject* rhs) { return myMap[lhs].componentID < myMap[rhs].componentID; });

如果您的编译器不支持lambdas,只需将它们重写为自由函数或函数对象。我只是简单地使用lambdas。

为了表现,我最初可能reserve在矢量中有足够的空间,而不是让它逐渐扩展。

(另请注意,我还没有测试过代码,所以可能需要一点点摆弄)

另外,我不知道这个地图应该代表什么,但是拿着一个地图,其中键和值类型都是指针真的设置我的“坏C ++”感觉刺痛。它闻到了手动内存管理和混乱(或不存在)所有权语义。

您提到在list中获得输出,但vector几乎肯定是性能更好的选项,所以我使用了它。 list更可取的唯一情况是,当你无意迭代它时,如果你需要保证指针和迭代器在修改列表后保持有效。

答案 1 :(得分:1)

我认为你会做得很好:

GetObjectList(std::list<Object*> &objects)
{
  std::vector <Object*> vec;
  vec.reserve(map.size());
  for(auto it = map.begin(), it_end = map.end(); it != it_end; ++it)
    vec.push_back(it->second);
  std::sort(vec.begin(), vec.end(), [](Object* a, Object* b) { return a->ID < b->ID; });
  objects.assign(vec.begin(), vec.end());
}

答案 2 :(得分:1)

首先,我不会使用std::list,而是使用std::vector。现在,就特殊问题而言,您需要执行两项操作:生成容器,按照您的标准对其进行排序。

// Extract the data:
std::vector<Object*> v;
v.reserve( m.size() );
std::transform( m.begin(), m.end(), 
                std::back_inserter(v),
                []( const map<Object*, baseObject*>::value_type& v ) {
                     return v.first;
                } );
// Order according to the values in the map
std::sort( v.begin(), v.end(), 
           [&m]( Object* lhs, Object* rhs ) {
               return m[lhs]->id < m[rhs]->id;
           } );

如果没有C ++ 11,您将需要创建仿函数而不是lambdas,如果您坚持要返回std::list,那么您应该使用std::list<>::sort( Comparator )。请注意,这可能效率低下。如果性能是一个问题(在您开始工作并且您了解并且知道这确实是一个瓶颈之后),您可能需要考虑使用中间map<int,Object*>

std::map<int,Object*> mm;
for ( auto it = m.begin(); it != m.end(); ++it )
   mm[ it->second->id ] = it->first;
}
std::vector<Object*> v;
v.reserve( mm.size() );                  // mm might have less elements than m!
std::transform( mm.begin(), mm.end(), 
                std::back_inserter(v),
                []( const map<int, Object*>::value_type& v ) {
                     return v.second;
                } );

同样,这可能比原始版本更快或更慢......个人资料。

答案 3 :(得分:1)

以下是如何执行您所说的内容,“按照baseObject.ID的顺序对其进行排序”:

typedef std::map<Object*, baseObject*> MapType;
MapType mymap; // don't care how this is populated
               // except that it must not contain null baseObject* values.

struct CompareByMappedId {
    const MapType &map;
    CompareByMappedId(const MapType &map) : map(map) {}
    bool operator()(Object *lhs, Object *rhs) {
        return map.find(lhs)->second->ID < map.find(rhs)->second->ID;
    }
};

void GetObjectList(std::list<Object*> &objects) {
    assert(objects.empty()); // pre-condition, or could clear it
    // or for that matter return a list by value instead.

    // copy keys into list
    for (MapType::const_iterator it = mymap.begin(); it != mymap.end(); ++it) {
        objects.push_back(it->first);
    }
    // sort the list
    objects.sort(CompareByMappedId(mymap));
}

这并不是非常有效:它在地图中查找的内容比严格必要的更多,并且操纵std::list::sort中的列表节点可能比std::sort操纵随机时慢一点-access指针容器。但是,std::list本身对于大多数用途来说效率不高,所以你预计设置它会很昂贵。

如果您需要进行优化,可以创建一对(int, Object*)对的向量,这样您只需迭代一次地图,无需查看。对对进行排序,然后将每对的第二个元素放入列表中。这可能是一个不成熟的优化,但它在实践中是一个有效的技巧。

答案 4 :(得分:0)

我会创建一个新的地图,其中包含使用对象的组件ID的排序标准。从第一个地图填充第二个地图(只需迭代或std :: copy in)。然后,您可以使用迭代器按顺序读取此映射。

在插入使用向量或列表(log(n)时间而不是常量时间)方面略有开销,但它避免了在创建好的向量或列表后进行排序的需要。

此外,您稍后可以在程序中添加更多元素,并且无需借助即可维护其订单。

答案 5 :(得分:0)

我不确定我是否完全理解您要在地图中存储的内容,但也许look here

std :: map的第三个模板参数是一个较少的仿函数。也许您可以利用它来在插入时对存储在地图中的数据进行排序。然后它将是地图迭代器上的直接循环以填充列表