为什么我的“从n选择k”算法适用于std :: vector而不是std :: map?

时间:2017-03-02 15:34:42

标签: c++ templates dictionary iterator

我需要一种迭代器,它允许我迭代容器中的所有元素组合(允许重复和{1,2}=={2,1})。我写了这个:

#include <iostream>
#include <map>
#include <vector>

template <typename T>
struct ChooseKfromN{
    T   mbegin;
    T   mend;
    std::vector<T> combo;
    ChooseKfromN(T first,T last,int k) : mbegin(first),mend(last),combo(k,first) {}
    bool increment(){
        for (auto it = combo.begin();it!=combo.end();it++){
            if (++(*it) == mend){
                if (it != combo.end()){
                    auto next  = it;
                    next++;
                    auto nexit = (*next);
                    nexit++;
                    std::fill(combo.begin(),next,nexit);
                }
            } else  { return true;}
        }
        std::cout << "THIS IS NEVER REACHED FOR A MAP !! \n" << std::endl;
        return false;
    }
    typename std::vector<T>::const_iterator begin(){ return combo.begin();}
    typename std::vector<T>::const_iterator end()  { return combo.end();}
};

template <typename T>
ChooseKfromN<T> createChooseKfromN(T first,T last,int k) {
    return ChooseKfromN<T>(first,last,k); 
}

std::map ...

上使用此功能
int main(){
    //std::vector<std::string> xx = {"A","B","C"};
    std::map<int,int> xx;
    xx[1] = 1;
    xx[2] = 2;

    auto kn = createChooseKfromN(xx.begin(),xx.end(),2);
    int counter = 0;
    do {
        for (auto it = kn.begin();it != kn.end();it++){
            std::cout << (**it).first << "\t";
        }
        std::cout << "\n";
        counter++;
    } while(kn.increment());
    std::cout << "counter = " << counter << "\n";
}

...我收到运行时错误。使用向量时,我得到正确的输出(另请参阅here):

A   A   
B   A   
C   A   
B   B   
C   B   
C   C   
THIS IS NEVER REACHED FOR A MAP !! 

counter = 6

为什么std::mapstd::vector一起使用时会中断?

2 个答案:

答案 0 :(得分:2)

Valgrind报道

==32738== Invalid read of size 8
==32738==    at 0x109629: ChooseKfromN<std::_Rb_tree_iterator<std::pair<int const, int> > >::increment() (42559588.cpp:17)
==32738==    by 0x108EFE: main (42559588.cpp:43)
==32738==  Address 0x5a84d70 is 0 bytes after a block of size 16 alloc'd
==32738==    at 0x4C2C21F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==32738==    by 0x10B21B: __gnu_cxx::new_allocator<std::_Rb_tree_iterator<std::pair<int const, int> > >::allocate(unsigned long, void const*) (new_allocator.h:104)
==32738==    by 0x10B113: std::allocator_traits<std::allocator<std::_Rb_tree_iterator<std::pair<int const, int> > > >::allocate(std::allocator<std::_Rb_tree_iterator<std::pair<int const, int> > >&, unsigned long) (alloc_traits.h:416)

看第17行,就在这里:

            if (it != combo.end()){
                auto next  = it;
                next++;
                auto nexit = (*next);  // Line 17
                nexit++;

如果next == combo.end(),则*next是无效的引用。

答案 1 :(得分:1)

使用位图方法修复代码(请参阅definition

我正在维护2个向量,一个是我们可以置换的启用索引的位图,另一个是容器中项目的引用向量。

我还提供了模板emit()函数,因此相同的通用代码可以在地图,矢量,集合,无序映射等上运行。

#include <iostream>
#include <map>
#include <vector>
#include <functional>
#include <iterator>


template <typename Iter>
struct ChooseKfromN
{
    using iterator_type = Iter;
    using value_type = typename std::iterator_traits<iterator_type>::value_type;
    using result_type = std::vector<std::reference_wrapper<const value_type>>;

    result_type values_;
    std::vector<bool> bitset_;
    int k_;

    ChooseKfromN(Iter first,Iter last,int k)
        : values_(first, last)
        , bitset_(values_.size() - k, 0)
        , k_(k)
    {

        std::reverse(values_.begin(), values_.end());
        bitset_.resize(values_.size(), 1);
    }

    result_type& get(result_type& target) const
    {
        target.clear();

        for (std::size_t i = bitset_.size() ; i ; ) {
            --i;
            if (bitset_[i]) {
                target.push_back(values_[i]);
            }
        }
        return target;
    }

    bool increment(){
        return std::next_permutation(bitset_.begin(), bitset_.end());
    }
};

template <typename T>
ChooseKfromN<T> createChooseKfromN(T first,T last,int k) { return ChooseKfromN<T>(first,last,k); }


template<class T>
std::ostream& emit_key(std::ostream& os, const T& t)
{
    return os << t;
}

template<class K, class V>
std::ostream& emit_key(std::ostream& os, const std::pair<const K, V>& kv)
{
    return os << kv.first;
}

template<class Container>
void test(Container const& c)
{
    auto kn = createChooseKfromN(c.begin(),c.end(),2);
    using ChooserType = decltype(kn);
    using ResultType = typename ChooserType::result_type;

    int counter = 0;
    ResultType result_buffer;
    do {
        kn.get(result_buffer);
        for (auto&& ref : result_buffer)
        {
            emit_key(std::cout, ref.get()) << '\t';
        }
        std::cout << "\n";
        counter++;
    } while(kn.increment());
    std::cout << "counter = " << counter << "\n";
}


int main(){
    std::map<int,int> xx;
    xx[1] = 1;
    xx[2] = 2;
    xx[3] = 3;
    xx[4] = 4;

    std::vector<std::string> yy = {"A","B","C"};

    test(xx);
    test(yy);
}

预期产出:

1   2   
1   3   
2   3   
1   4   
2   4   
3   4   
counter = 6
A   B   
A   C   
B   C   
counter = 3