【问题标题】:C++ How to traverse through different set elements of a vector in specific patternC ++如何以特定模式遍历向量的不同集合元素
【发布时间】:2018-11-03 14:43:07
【问题描述】:

我有一个包含整数的集合向量,如下所示:

std::vector<std::set<int> > vec = {{2,4},{1,3,8},{7,5}};

上述集合向量示意图

| 2 | 4 |       
| 1 | 3 | 8 |
| 7 | 5 |

我需要遍历向量的每个集合元素,以便向量行中的每个集合元素访问下一行中的每个集合元素,依此类推。

例如,向量第一行的集合的第一个元素(即2)将访问第二行的第一个元素(即1),然后访问第三行的第一个元素(即7)。同样,这些遍历的顺序如下:

First vector row and first set element -> Second vector row and first set element -> Third vector row and first set element
First vector row and first set element -> Second vector row and first set element -> Third vector row and second set element
First vector row and first set element -> Second vector row and second set element -> Third vector row and first set element
First vector row and first set element -> Second vector row and second set element -> Third vector row and second set element 
First vector row and first set element -> Second vector row and third set element -> Third vector row and first set element
First vector row and first set element -> Second vector row and third set element -> Third vector row and second set element

First vector row and second set element -> Second vector row and first set element -> Third vector row and first set element
First vector row and second set element -> Second vector row and first set element -> Third vector row and second set element
First vector row and second set element -> Second vector row and second set element -> Third vector row and first set element
First vector row and second set element -> Second vector row and second set element -> Third vector row and second set element 
First vector row and second set element -> Second vector row and third set element -> Third vector row and first set element
First vector row and second set element -> Second vector row and third set element -> Third vector row and second set element

生成的向量应该是列表的向量,其每个元素如下:

std::vector<std::list<int> > list = {{2,1,7},{2,1,5},{2,3,7},{2,3,5},{2,8,7},{2,8,5},{4,1,7},{4,1,5},{4,3,7},{4,3,5},{4,8,7},{4,8,5}};

列表合成向量示意图

| 2 | 1 | 7 |
| 2 | 1 | 5 | 
| 2 | 3 | 7 |
| 2 | 3 | 5 |
| 2 | 8 | 7 |
| 2 | 8 | 5 |
| 4 | 1 | 7 |
| 4 | 1 | 5 | 
| 4 | 3 | 7 |
| 4 | 3 | 5 |
| 4 | 8 | 7 |
| 4 | 8 | 5 |

在 C++ 中实现这一目标的最有效方法是什么?

【问题讨论】:

  • 请描述所需的模式,不要让我们从样本中猜测。此外,在您的样本中,是单独的集合行还是列?是 [{2,1,7}, {4,3,5} {8}]?还是其他组合?
  • AFAIK 没有现成的解决方案来计算标准库中的组合(这很糟糕,因为这个问题经常在 SO 上弹出)。可以采用 this 方法来处理所需的容器类型,或者基于迭代器使其通用。
  • 感谢你们所有的努力,我已经编辑了我的描述以使其更容易理解。
  • 输出中的最后一个数字是否意味着75 之前?
  • @Caleth 没必要,您的解决方案提供了所需的输出。您能否分享一下它的时间复杂度?

标签: c++ algorithm traversal


【解决方案1】:

让我们把它分解成几个步骤:

// Add the int to a copy of the list
std::list<int> append_copy(std::list<int> l, int i)
{
    l.push_back(i);
    return l;
}

// append_copy the list for each element in the set
template<typename OutputIterator>
OutputIterator cross_append(const std::set<int>& s, const std::list<int>& l, OutputIterator d_first)
{
    return std::transform(s.begin(), s.end(), d_first, [&](int i){ return append_copy(l, i); });
}

std::vector<std::list<int> > cross_apply(const std::vector<std::set<int> > & vec)
{
    // start with a single empty list
    std::vector<std::list<int> > result{ {} };

    // loop over the input to get the sets
    for (auto& s : vec)
    {
        std::vector<std::list<int> > inner;
        auto it = std::back_inserter(inner);

        // loop over the last run's intermediate, duplicating it
        for (auto& l : result)
        {
             it = cross_append(s, l, it);
        }
        result = inner;
    }
    return result;
}

See it live

【讨论】:

  • 感谢@Caleth,非常感谢!
【解决方案2】:

标准库中没有现成的解决方案可用于枚举来自多个 AFAIK 集合的元素组合。

这是next_combination 函数的实现,其工作方式类似于std::next_permutation

// advance to next combination, return false if already last combination
template <typename SetOfSetsIter, typename CombinationIter>
bool next_combination(
    CombinationIter combFirst, SetOfSetsIter dataFirst, SetOfSetsIter dataLast)
{
    for(; dataFirst != dataLast; ++dataFirst, ++combFirst)
    {
        if(++(*combFirst) != dataFirst->end())
            return true;
        *combFirst = dataFirst->begin();
    }
    return false;
}

// make combination from first elements of set's sets as a vector
template <typename SetOfSetsIter>
std::vector<typename std::iterator_traits<SetOfSetsIter>::value_type::const_iterator>
first_combination(SetOfSetsIter dataFirst, SetOfSetsIter dataLast)
{
    std::vector<typename std::iterator_traits<SetOfSetsIter>::value_type::const_iterator>
        combination;
    for(; dataFirst != dataLast; ++dataFirst)
        combination.push_back(dataFirst->cbegin());
    return combination;
}

用法:

typedef std::vector<int> Set;
typedef std::vector<Set> SetOfSets;
const SetOfSets data = {{2, 4}, {1, 3, 8}, {7, 5}};
std::vector<Set::const_iterator> comb = first_combination(data.cbegin(), data.cend());
std::cout << "First to last:" << std::endl;
do
{
    for(const auto& it : comb)
        std::cout << *it << " ";
    std::cout << std::endl;
} while(next_combination(comb.begin(), data.cbegin(), data.cend()));
comb = first_combination(data.cbegin(), data.cend());

std::cout << "\nLast to first:" << std::endl;
do
{
    for(const auto& it : comb)
        std::cout << *it << " ";
    std::cout << std::endl;
} while(next_combination(comb.rbegin(), data.crbegin(), data.crend()));

Live Demo

【讨论】:

  • 这看起来是使用 Java 风格的迭代而不是 C++ 风格的迭代
  • @Caleth 你的意思是 MultiSetCombinator 可以是一个合适的自定义迭代器吗?如果是,我没有用 C++ 编写自定义迭代器的经验。但很高兴看到在这种情况下如何做到这一点。
  • 你将next的增量部分移动到operator++,将booly部分移动到operator==,并为范围的末尾提供一个默认构造函数,然后添加一堆typedef这样std::iterator_traits 就会得到正确的实例化
  • @Caleth 经过深思熟虑后,我更新了答案以遵循类似std::next_permutation 的方法(更适合 IMO)
猜你喜欢
  • 1970-01-01
  • 2018-09-04
  • 1970-01-01
  • 1970-01-01
  • 2019-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
相关资源
最近更新 更多