【问题标题】:How to add elements to a sorted list ...? (C++)如何将元素添加到排序列表...? (C++)
【发布时间】:2014-10-31 13:30:37
【问题描述】:

我刚刚第一次接触 std::vector,现在我想改变我使用纯 C 样式数组的坏习惯。我发现 std::list 是排序时使用的容器。但是,我不是 100% 如何做到以下几点:

我正在做一些计算,其结果取决于两个指​​标(i 和 j)。最后,我只对 100 个最小的结果感兴趣(不一定是 100,但肯定比我的计算值总数小得多,下面的代码中的 m*n)。

const int L = 100;
int i_list[L];
int j_list[L];
double value_list[L];

for (int i=0;i<m;i++){
    for (int j=0;j<n;j++){
        double x = doSomeCalculations(i,j);
        insertTheValueAndIndices(i,j,x,i_list,j_list,value_list);
    }
}

完成后,value_list 应该包含 100 个最小值(递增顺序)和 i_list / j_list 对应的索引。我有一个“insertValuesAndIndices()”的工作版本,但在那里我使用普通数组和插入新值的最低效的方式。在写作时,我意识到我实际上有两个不同的问题:

  1. 计算值的数量 (m*n) 远远大于我想要保留在列表中的数量,因此简单地保留所有值并最终只排序一次并不是一个真正的选择。另一方面,我最终只需要正确顺序的结果,所以也许有一种方法可以只对列表进行一次排序。这种排序有什么“标准”的聪明有效的方法吗?

  2. 即使我可以存储所有结果并在之后进行排序,我也不确定如何使用 std::list.sort() 以正确的顺序获取索引数组。我想到的是定义一些包含结果和两个索引的类,将这些元素放在一个列表中,然后使用一个只检查值的比较器进行排序。但是,也许有更直接的方法可以做到这一点?

提前致谢

【问题讨论】:

  • std::vector可以用std::sort排序。
  • "std::list 是排序时使用的容器" -- 嗯?你为什么这么说?任何序列容器都可以排序。
  • 当然,您实际上并不需要排序。只需使用std::lower_bound 即可找到插入点。这样,您不必让向量增长到超出您需要的值。
  • @Drop No. 在某些情况下它是唯一的容器。插入时考虑迭代器的有效性。

标签: c++ arrays sorting


【解决方案1】:

首先,您可能不需要std::list,而是std::vector。 然后使用std::lower_bound 查找插入位置,如果 结果向量包含的元素数量超过 你有兴趣,std::vector&lt;&gt;::pop_back 摆脱 额外的。

【讨论】:

  • c++.com 说:“因为向量使用数组作为其底层存储,在向量末端以外的位置插入元素会导致容器将位置之后的所有元素重新定位到新位置。与其他类型的序列容器(例如 list 或 forward_list)为相同操作执行的操作相比,这通常是一种低效的操作。”大多数时候我不会在最后插入,所以我不明白,为什么我应该使用向量而不是列表...
  • @tobi303 因为您引用的段落在现代机器上是错误的。您可以在执行一次分配所需的时间内复制大量数据,并且在复制之后,所有数据仍将是连续的,从而减少了缓存未命中。除此之外,您需要一个随机访问迭代器来执行下界,这是对std::list 的又一次打击。
【解决方案2】:

感谢你们的cmets。由于我的帖子没有说明一个明确定义的问题,而是表明我对如何解决我的问题的无知;),我想发布我的结论和一个工作示例......

首先,感谢您的澄清,“std::list 是排序时使用的容器” 不是真的。正如所指出的,例如由 James Kanze 撰写,std::vector 也可以完成这项工作。其次,如果我只是在正确的位置“插入”新值,我真的不必对向量进行“排序”。此外,没有人反对我对如何对索引进行排序的想法,这是我的解决方案:

#include <cfloat>
#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>

struct MyValue{
    MyValue(double v,int f,int s):value(v),first(f),second(s){}
    double value;
    int first;
    int second;
};
bool compareValues(const MyValue a,const MyValue b){return a.value < b.value;}
void insertMyValue(std::vector<MyValue>* v,MyValue x,int maxSize){
    v->insert(std::lower_bound(v->begin(),v->end(),x,compareValues),x);
    if (v->size()>maxSize){v->erase(v->end()-1);}
}
void main(){
    const int imax = 10;
    const int jmax = 10;
    const int Nmax = 30;
    std::vector<MyValue> results;
    results.reserve(Nmax+1);
    // Fill the vector
    for (int i=0;i<imax;i++){
        for (int j=0;j<jmax;j++){
            double result = i*(j+0.1);
            insertMyValue(&results,MyValue(result,i,j),Nmax);
        }
    }
    // Print it
    for (std::vector<MyValue>::iterator it = results.begin();it<results.end();it++){
        cout << (*it).first << " " << (*it).second << " " << (*it).value << endl;
    }
}

【讨论】:

    【解决方案3】:

    排序是个坏主意。

    改为使用std::vector&lt;int&gt; buffer(count); 来存储std::make_heap

    然后做:

    template<class T>
    void add_element( std::vector<T>& heap, T element, size_t size ) {
      heap.push_back(element);
      std::push_heap(heap.begin(), heap.end());
      while (heap.size() > size) {
        std::pop_heap(heap.begin(), heap.end());
        heap.pop_back();
      }
    }
    

    它以vector 的形式获取一个堆(一个空的vector 是一个堆),并添加一个元素,然后弹出任何超过堆限制的元素。

    这适用于任何具有operator&lt;T。还有push_heappop_heap 的自定义比较器版本。

    【讨论】:

      猜你喜欢
      • 2018-08-31
      • 2013-11-20
      • 1970-01-01
      • 1970-01-01
      • 2015-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-16
      相关资源
      最近更新 更多