根据条件拆分STL列表

时间:2014-05-21 22:08:21

标签: c++ algorithm list vector stl

我有一个std :: list,如下所示(x标记表示某个数字小于500)

x,x,x,x,503,x,x,x,510,x,x,x,502,x,x,x,x,x,x,600 - std::list<int> originallist

我希望将列表拆分为列表std::vector<std::list<int> >的向量,如下所示

1st element of vector: x,x,x,x,503
2nd element of vector: x,x,x,510
...
...
last element of vector: x,x,x,x,x,x,600

我现在的代码如下:

list<int> templist; vector<list<int> > v;
for(list<int>::iterator lit=originallist.begin(); lit!=oriniallist.end(); ++lit) {
    if (*lit > 500) {
        templist.push_back(*lit);v.push_back(templist); templist.clear(); continue;
    }
    templist.push_back(*lit);
}

在不使用templist的情况下,在c ++中实现上述任务的最有效方法是什么。任何帮助表示赞赏。

4 个答案:

答案 0 :(得分:4)

虽然这个解决方案确实使用了临时std::list,但它没有分配列表节点元素,并且在C ++ 03情况下完成了1个内存分配(C ++ 11情况下进行了对数的内存分配)关于返回值的大小)

这是一个C ++ 03解决方案。 C ++ 11解决方案可以一次性完成。

bool big_as_500( int x ) {return x>=500;}

std::vector< std::list< int > > do_stuff( std::list<int>& original_list ) {
  // we have to do this, because resizing the return value involves lots of allocations
  // and stuff in C++03, so make sure we get the size right by precalculating it:
  std::size_t count = std::count_if( originallist.begin(), originallist.end(), big_as_500 );
  std::vector< std::list< int > > result;
  result.reserve(count+1); 

  typedef std::list<int>::const_iterator const_iterator;
  std::list< int > current;
  for(const_iterator it= originallist.begin(); it!=originallist.end();/*nothing*/) {
    ++it; // about to invalidate it! (or move lists)
    current.splice( current.end(), originallist, originallist.begin() ); // O(1) no memory allocation
    if (big_as_500(current.back())) {
      result.push_back( std::list<int>() );
      current.swap( result.back() );
    }
  }
  // original problem does not specify what to do if the original list does not end
  // with an element "big_as_500", so I'll just drop them
  return result; // rely on NRVO to eliminate the copy here, if your compiler does not
  // support it, take result as a reference parameter.
}

C ++ 11解决方案:

std::vector< std::list< int > > do_stuff( std::list<int>& original_list ) {
  std::vector< std::list< int > > result;

  typedef std::list<int>::const_iterator const_iterator;
  std::list< int > current;
  for(const_iterator it= originallist.begin(); it!=originallist.end();/*nothing*/) {
    ++it;// about to become invalid/in wrong list
    current.splice( current.end(), originallist, originallist.begin() ); // O(1) no memory allocation
    if (current.back() >= 500) {
      result.emplace_back( std::move(current) );
    }
  }
  // original problem does not specify what to do if the original list does not end
  // with an element "big_as_500", so I'll just drop them
  return result; // will NRVO, or move, so no worries
}

在C ++ 11中,调整大小相对便宜,所以我们很好。

现在,我们可以在C ++ 03中真正看中并模仿C ++ 11所做的事情并一次完成所有这些。

template<typename T, typename A>
void efficient_grow_by_1( std::vector<T,A>& make_one_bigger ) {
  if (make_one_bigger.size()+1 > make_one_bigger.capacity() )
  {
    std::vector<T, A> swap_vec;
    swap_vec.reserve( (make_one_bigger.size()+1)*5/3 );
    for (std::vector<T, A>::iterator it = make_one_bigger.begin(); it != make_one_bigger.end(); ++it ) {
      using std::swap;
      swap_vec.push_back();
      std::swap( *it, swap_vec.back() );
    }
    swap_vec.swap( make_one_bigger );
  }
  make_one_bigger.push_back();
}
void do_stuff( std::list<int>& original_list, std::vector< std::list< int > >& result ) {
  typedef std::list<int>::const_iterator const_iterator;
  std::list< int > current;
  for(const_iterator it= originallist.begin(); it!=originallist.end();) {
    ++it;
    current.splice( current.end(), originallist, originallist.begin() ); // O(1) no memory allocation
    if (current.back()>=500) {
      efficient_grow_by_1(result);
      current.swap( result.back() );
    }
  }
  // original problem does not specify what to do if the original list does not end
  // with an element "big_as_500", so I'll just drop them
}

这是相当疯狂的,所以我建议升级你的编译器。

这里的诀窍是我们填充了临时的&#39;列表中包含一个元素一次splice。因为std::list::splice的(大多数?)实现最终必须遍历元素来计算它们(它在C ++ 11中是必需的,在C ++ 03中是常见的),一次一个地执行它因为我们确定我们想要将哪些元素放入下一个块中是合理有效的。每个节点直接来自输入列表,并被收集到临时列表中(无内存分配)。

我们构建此列表后,直接swap将其添加到vector的输出list中。这避免了任何内存分配,除了保存list的(相对较小的)基础数据所需的内存分配。

在C ++ 03中,我们要么做两遍解决方案并预先计算输出std::vector的大小,要么我们通过仔细增长来模拟C ++ 11 move效率swap 1}}包含list的机制。您的std库实现可能已经假装了这一点,但我不确定旧库中的swap - resize优化有多常见。

保持一次通过可能值得第二个C ++ 03和C ++ 11解决方案使用的对数分配:走std::list是缓存未命中的练习。

答案 1 :(得分:1)

第三版

此版本使用std::list::splice并移动迭代器,直到找到delim iter或end()到达。

#include <iostream>
#include <list>
#include <vector>

std::vector< std::list<int> > & split( std::list<int>  v,
                   int delim, std::vector< std::list<int> >& elems) {

    auto it = v.begin();

    while ( it != v.end()) {
        std::list<int> l;

        while ( it != v.end() && *it < delim) {
            ++it;
        }

        if( it != v.end()) {
            l.splice( l.begin(), v, v.begin(), ++it);
            it = v.begin();
        } else {
            l.splice( l.begin(), v, v.begin(), it);
        }

        elems.push_back( l);
    }
    return elems;
}


std::vector< std::list<int> > split( const std::list<int>  &v, int delim) {
    std::vector< std::list<int> > elems;
    split( v, delim, elems);
    return elems;
}

用法:

int main() {

    std::list<int> v = { 1, 2, 3, 503, 5, 6, 502, 7, 510, 3, 500, 6, 7};
    std::vector< std::list<int> > vl;
    vl = split( v, 500);

    int i = 0;
    while( i < vl.size()) {
        std::list<int>::const_iterator it = vl[ i].begin();
        while( it !=  vl[ i].end())
            std::cout << *it++;
        std::cout << std::endl;
        ++i;
    }

    return 0;
}

http://ideone.com/VRpGft

打印:

123503

56502

7510

3500

67

第一个版本

此版本使用std::list::splice

#include <iostream>
#include <list>
#include <vector>

std::vector< std::list<int> > & split( std::list<int>  v,
                   int delim, std::vector< std::list<int> >& elems) {

    auto it = v.begin();

    while ( it != v.end()) {
        std::list<int> l;
        auto it3 = l.begin();
        while ( it != v.end() && *it < delim) {
            l.splice( it3, v, it);
            it = v.begin();
        }
        if( it != v.end()) {
            l.splice( it3, v, it);
            it = v.begin();
        }
        elems.push_back( l);
    }
    return elems;
}


std::vector< std::list<int> > split( const std::list<int>  &v, int delim) {
    std::vector< std::list<int> > elems;
    split( v, delim, elems);
    return elems;
}

用法:

int main() {

    std::list<int> v = { 1, 2, 3, 503, 5, 6, 502, 7, 510, 3, 500, 5, 9};
    std::vector< std::list<int> > vl;
    vl = split( v, 500);

    int i = 0;
    while( i < vl.size()) {
        std::list<int>::const_iterator it = vl[ i].begin();
        while( it !=  vl[ i].end())
            std::cout << *it++;
        ++i;
    }

    return 0;
}

打印:

123503565027510350059

http://ideone.com/1xMehy

第二版

这是不使用std::list::splice功能的简化版本。此函数将元素放在迭代器之前,因此必须稍微更改循环。

#include <iostream>
#include <list>
#include <vector>

std::vector< std::list<int> > & split( const std::list<int>  & v,
                   int delim, std::vector< std::list<int> >& elems) {

    std::list<int>::const_iterator it = v.begin();

    while ( it != v.end()) {

        std::list<int> l;
        while ( it != v.end() && *it < delim) {
            l.push_back( *it++);
        }

        if( it != v.end()) l.push_back( *it++);
        elems.push_back( l);
    }

    return elems;
}


std::vector< std::list<int> > split( const std::list<int>  &v, int delim) {
    std::vector< std::list<int> > elems;
    split( v, delim, elems);
    return elems;
}

用法:

int main() {

    std::list<int> v = { 1, 2, 3, 503, 5, 6, 502, 7, 510, 3, 500, 5, 9};
    std::vector< std::list<int> > vl;
    vl = split( v, 500);

    int i = 0;
    while( i < vl.size()) {

        std::list<int>::const_iterator it = vl[ i].begin();

        while( it !=  vl[ i].end())
            std::cout << *it++;

        ++i;
    }

    return 0;
}

打印:

123503565027510350059

http://ideone.com/MBmlLE

答案 2 :(得分:0)

尝试以下

#include <vector>
#include <list>
#include <algorithm>
#include <functional>

//...

auto first = YourList.begin();

while ( first != YourList.end() )
{
   auto last = std::find_if( first, YourList.end(), std::bind2nd( std::greater<int>(), 500 ) );
   if ( last != YourList.end() ) ++last;

   YourVector.push_back( std::list<int>( first, last ) );

   first = last;
} 

答案 3 :(得分:0)

  1. 循环显示数字并获取需要拆分列表的位置之间的距离。

  2. 在列表中为每个拆分位置使用拼接功能:

    lst.splice( newLst.begin(), newLst, lst.begin(), lst.begin() + sliceLength);
    

    http://www.cplusplus.com/reference/list/list/splice/

  3. (请注意,拼接会破坏原始列表)