根据列表的属性从列表中选择元素

时间:2015-06-19 19:00:51

标签: c++ performance search data-structures

我正在开发一个旨在渲染大量形状的应用程序。每个形状都可以分配给特定的图层

我将输入数据作为形状列表获取,对于每个形状,我有一个string属性,表示形状所属的图层。

现在,我需要开发一种方法,允许我只选择(绘制)属于给定所选图层列表的形状。 在伪代码中:

void draw_if(sorted_list shapes, list<string> selected_layers)
{
   for each shape in shapes
   {
      if (shape.layer in selected_layers)
        shape.draw();
   }
}

关键是我想尽快执行此操作;因此,我需要选择正确的数据结构和正确的算法。

所选图层列表是一个字符串列表(1÷100个不同的图层),但如果出于性能原因需要,可以将其转换为其他数据类型。

形状按照z顺序排序。

2 个答案:

答案 0 :(得分:1)

这里经常忽略基本的侵入式解决方案,以寻找精细的数据结构和算法,但通常是最快的。

假设您别无选择,只能将选择分开,如果您想要一个非常快速的解决方案,请在每一层中存储一个布尔选择标志(可以是一个位)。形成选择时,除了形成列表外,还要设置这些标记。取消选择图层不仅会将其从选择中删除,还会将该选择标记设置为false。

接下来,将用于指示所选图层的字符串转换为索引到随机访问结构中(例如:std::vector或者甚至是普通的旧数组(如果可以在编译时确定大小),就像这样(简化) ):

struct Layer
{
    string name;

    // Set this to true when the layer is selected, false 
    // when it is deselected. Use atomics if thread safety 
    // is required.
    bool selected;
};

...并将shape.layer转换为图层的索引(或指针/迭代器)。如果您别无选择,只能从图层字符串开始,以识别形状所属的图层,因为您最初会给出字符串输入(例如:从您正在加载的文件中),然后将这些字符串转换为图层索引/指针/迭代器当你从那些字符串输入创建形状时。在这里使用哈希表或至少std :: set / map(初始形状构造上的字符串搜索应该是对数或更好)将这些图层字符串转换为图层索引/指针/迭代器。

如果除了图层选择状态之外还需要图层选择列表,则可以执行此操作(伪代码):

void select(Layer layer, LayerList& layer_selection)
{
     if (!layer.selected)
     {
          layer.selected = true;
          layer_selection.insert(&layer);
     }
}

void deselect(Layer layer, LayerList& layer_selection)
{
     if (layer.selected)
     {
          layer.selected = false;
          layer_selection.erase(&layer);
     }
}

...您的图层选择将索引/指针/迭代器存储到图层。选择和取消选择列表插入/删除都可以在恒定时间内完成(即使在最坏情况下),而不需要散列开销,同时保留插入顺序如果您对图层选择感兴趣并使用固定的allocator(这是一个涉及放置新的,联合和内存池的复杂主题,所以如果需要我会深入研究它,但暂时省略它以简洁)。

现在你的主要伪代码变成了这样的东西:

void draw_if(list shapes, list layers)
{
   for each shape in shapes
   {
      if (layers[shape.layer].selected)
        shape.draw();
   }
}

...或者如果您使用指针/迭代器:

void draw_if(list shapes, list layers)
{
   for each shape in shapes
   {
      if (shape.layer->selected)
        shape.draw();
   }
}

在性能方面很难超越这一点,因为即使最优化的哈希表也无法超越简单的索引数组访问内存,除了哈希之外你仍然需要访问它。现在,如果您可以通过选择/取消选择图层的过程来巩固“选定形状”的概念并预先形成所选形状,那么您可以这样做:

void draw_selected(list selected_shapes)
{
   for each shape in selected_shapes
       shape.draw();
}

......如果形成所选形状列表的额外成本通过在必须改变之前重复使用来补偿,则可以更快。请注意,在这种情况下,您仍希望将这些字符串转换为索引,因为您不希望“选定形状”列表必须是简单数组。要形成选定的形状列表:

ShapeList selected_shapes(ShapeList all_shapes, LayerList layers)
{
     // Forming this in advance will help if it is reused for
     // numerous drawing frames before it needs to change (ex:
     // before the Z-order changes, before new elements are inserted
     // or existing ones removed, before the layer selection changes).
     ShapeList results;
     for each shape in all_shapes:
          if layers[shape.layer].selected)
             results.push_back(shape);
     return results;
}

...由于我们现在存储在图层中的选择状态,因此形成和访问(由于完美紧凑的形状选择数组的空间局部性)仍然比哈希表更便宜。

这可以保持一切对高速缓存友好,并避免使用像哈希表这样的昂贵(相对来说)数据结构,除了在初始字符串 - >索引/指针转换部分(你只需要在从字符串输入创建形状时这样做) 。在这种情况下,唯一需要进行任何类型搜索(对数或常量时间散列/特里)的地方是将这些形状图层字符串从用户输入转换为索引/指针/迭代器。其他一切都是O(1)(即使是最坏情况下的复杂性),甚至不需要散列。

答案 1 :(得分:0)

我建议对所选图层使用集合而不是列表,这样就可以执行二进制搜索以确定形状。图层位于所选图层中,插入同时保留顺序很快。使用列表对于保持二进制搜索所需的顺序是低效的。

另一种选择是找出一些哈希算法并只使用哈希映射。