迭代排序列表并计算不同的数字

时间:2017-10-18 06:27:21

标签: c++ list unique counting

我想迭代排序列表以获取不同数字的数量。

请在下面找到我的尝试。列表的大小为k*k。 在对列表进行排序时,我会比较连续项以识别重复项。

int count_distinct(list<int> v)
{
    int num = k*k;
    std::list<int>::iterator it;
    it = v.begin();
    for (int a=0; a<k*k-1; a++)
    {
        if(*it == *it+1)
            num--;
        it++;
    }

    return num;
}

我无法更改列表,因此std::list::unique()不是一个选项。复制列表或唯一项目对我来说太慢了。

6 个答案:

答案 0 :(得分:2)

如何使用std::set来获取唯一元素数?

size_t count_distinct(const list<int>& v)
{    
    std::set<int> temp (v.begin(), v.end());

    return temp.size(); 
}

答案 1 :(得分:2)

假设您要查找该列表中唯一整数的数量,并且列表未排序,您可以使用临时集或unordered_set,如下所示:

size_t count_distinct(list<int> v)
{
    std::unordered_set<int> distinct;
    for(auto &x : v)
    {
        distinct.insert(x);
    }
    return distinct.size();
}

答案 2 :(得分:2)

以下是用于提取所有唯一值的容器的解决方案 (因为你之前说过想要使用它们):

计算唯一值的方法:

template < typename T >
size_t count_unique(const std::list<T> & input)
{
    std::set<T> unique(input.begin(), input.end());
    return unique.size();
}

提取唯一值列表的方法:

template < typename T >
void unique(const std::list<T> & input, std::list<T> & output)
{
    std::set<T> unique(input.begin(), input.end());   
    std::copy(unique.begin(), unique.end(), std::back_inserter(output));
}

示例程序:

int main(int argc, char** argv)
{
    std::list<int> list = { 1, 3, 4, 10, 3, 1, 6, 7 };
    std::list<int> out;

    std::cout << count_unique(list) << std::endl;

    unique(list, out);
    for (auto & x : out)
        std::cout << x << std::endl;
}

答案 3 :(得分:2)

您的代码存在以下问题:

  1. 您可以按值将容器传递给函数。您应该通过const引用传递它以最小化速度和内存丢失。
  2. 您的条件*it == *it+1始终为假(您比较nn+1)。可能你想写*it == *(it+1)std::listbidirectional iterators而你不能+1
  3. 代码应该是这样的:

    size_t count_distinct(const std::list<int>& l) {
        if (l.empty()) return 0;
    
        size_t distinct = l.size();
        auto prev = l.begin();
    
        for (auto cur = std::next(prev); cur != l.end(); ++cur, ++prev) {
            if (*cur == *prev)
                --distinct;
        }
    
        return distinct;
    }
    

    或者您可以编写std::unique算法的修改版本:

    template<class ForwardIt>
    size_t unique_cnt(ForwardIt first, ForwardIt last) {
        if (first == last)
            return 0;
    
        size_t distinct = 1;    
        ForwardIt prev = first;
    
        while (++first != last) {
            if (!(*prev == *first)) {
                ++distinct;
            }
            prev = first;
        }
        return distinct;
    }
    

    然后简单地使用它

    size_t distinct = unique_cnt(l.begin(), l.end());         
    

    还有std::unique_copy +自定义迭代器方法,但它看起来不够优雅。

答案 4 :(得分:1)

对于排序数据,您可能不会比您尝试实施的直接方法更有效。

我更喜欢有类似的东西,因为我发现它更直观地向上计数而不是向下计数:

circles[i].y = circles[i].y > canvas.height ? 0 : circles[i].y + 1;

您还可以通过提供自定义OutputIterator来使std::unique_copy()算法计数而不是复制。但与上述方法相比,这在性能上几乎没有什么好处。当C ++ 17中的parallel implementations算法可用时,也许值得重新审视。

以下是一个例子:

std::size_t count_unique_sorted(std::list<int> const& l) {
    if (l.empty()) return 0;
    std::size_t count = 1;
    auto previous_value = l.front();
    // TODO: hope that the compiler fixes that redundant first comparison...
    for (auto next_value : l) {
        if (next_value != previous_value) {
            // the value changed! increment count and update previous_value
            ++count;
            previous_value = next_value;
        }
    }
    return count;
}

请注意,在这两种情况下,您都希望将列表作为const引用传递,而不是作为副本传递给函数。

如果您觉得这仍然很慢,请随意探索并行化的乐趣。这样做的好处可能取决于数据量和分布。所以你应该在那时开始一些系统的分析。

除非您需要对这些值进行重新排序,否则请考虑首先将数据转储到template <typename T> struct counter : public std::iterator<std::output_iterator_tag, T> { explicit counter(std::size_t& count) : count(count) {} counter& operator*() { return *this; } counter& operator++() { return *this; } void operator=(T const&) { ++count; } private: std::size_t& count; }; std::size_t count_unique_sorted2(std::list<int> const& l) { std::size_t count = 0; std::unique_copy(l.begin(), l.end(), counter<int>(count)); return count; } 。拥有随机访问迭代器简化了事情,拥有更好的局部性也可以加快速度......

答案 5 :(得分:0)

您可以使用std::list<int>::unique()获取vsize()中的所有不同元素来计算它们。必须对v进行排序。检查是否使用函数std :: is_sorted()对v进行了排序。如果不是 - 对它进行排序。这也意味着count_distinct不适用于const列表对象。

size_t count_distinct(list<int>& v)
{
    if (!is_sorted(v.begin(), v.end()))
    {
        v.sort();
    }
    v.unique();
    return v.size();
}