std::向量中的重复元素

Repeated elements in a std::vector

本文关键字:元素 向量 std      更新时间:2023-10-16

我有一个std::vector,我想检查其中的所有元素。如果某个元素出现多次,我会发出错误信号。

我就是这样做的:

std::vector<std::string> test;
test.push_back("YES");
test.push_back("YES");
for(int i = 0; i < test.size(); i++)
{
    if(test[i] > 1)
    {
        DCS_LOG_DEBUG("ERROR WITH COUNT")
    }
}

虽然我知道如何使用std::vector::count()方法进行计数,但这并不奏效。但我想得到每个元素的计数,而不是计算所有元素。。。有什么想法吗?

最简单的方法是std::sort矢量,然后使用std::adjacent_find


然而,如果你不想对向量进行排序,你可以在C++11:中这样做

#include <unordered_map>
#include <functional> // For std::hash<std::string>.
#include <string>
#include <iostream>
int main() {
    // Test data.
    std::vector<std::string> v;
    v.push_back("a");
    v.push_back("b");
    v.push_back("c");
    v.push_back("a");
    v.push_back("c");
    v.push_back("d");
    v.push_back("a");
    // Hash function for the hashtable.
    auto h = [](const std::string* s) {
        return std::hash<std::string>()(*s);
    };
    // Equality comparer for the hashtable.
    auto eq = [](const std::string* s1, const std::string* s2) {
        return s1->compare(*s2) == 0;
    };
    // The hashtable:
    //      Key: Pointer to element of 'v'.
    //      Value: Occurrence count.
    std::unordered_map<const std::string*, size_t, decltype(h), decltype(eq)> m(v.size(), h, eq);
    // Count occurances.
    for (auto v_i = v.cbegin(); v_i != v.cend(); ++v_i)
        ++m[&(*v_i)];
    // Print strings that occur more than once:
    for (auto m_i = m.begin(); m_i != m.end(); ++m_i)
        if (m_i->second > 1)
            std::cout << *m_i->first << ": " << m_i->second << std::endl;
    return 0;
}

此打印:

a: 3
c: 2

我实际上并没有对它进行基准测试,但这有机会表现得相当出色,原因如下:

  • 假设实际的向量元素不会产生病态的不平衡散列,这实际上是一种O(n)算法,而不是用于排序的O(n*log(n))
  • 我们使用指针到字符串的哈希表,而不是字符串本身,因此不会发生不必要的复制
  • 我们可以"预分配"哈希表桶(在构造m时传递v.size()),因此哈希表大小被最小化

特定元素

计数是标准的方法:

#include <algorithm>
...
    if (count (test.begin(), test.end(), "YES") > 1)
        std::cerr << "positiven";

如果你需要更多的性能,你可以用经典的方法:

bool exists = false;
for (auto const& v : test) {
    if (v == "YES") {
        if (exists) {
            std::cerr << "positiven";
            break;
        }
        else exists = true;
    }
}

任意元素多次

对于大矢量,请尝试std::set:

std::set<std::string> exists;
for (auto const &v : test) {
    if (!exists.insert(v).second)
        std::cerr << "positiven";
}

在这种方法中,如果你也想识别你是否已经提到了它的非唯一性,你可能想使用std::multiset:

const std::multiset<std::string> counts (test.begin(), test.end());
for (auto const &v: test)
    if (counts.count (v) == 2) std::cerr << "mehn";

如果容器很小,并且您只想查看是否有任何元素不止一次存在:

auto multitimes = [&test] (std::string const &str) {
    return count(test.begin(),test.end(),str)>1;
};
if (any_of (test.begin(), test.begin(), multitimes))
    std::cerr << "something was there more than oncen";

您可以使用std::map定义从键(字符串)到计数(int)的映射:

#include <map>
#include <string>
/* ... */
std::map<std::string, int> count_map;
/* ... */
count_map[key]++;

使用std::count对元素进行计数:http://www.cplusplus.com/reference/algorithm/count/

http://en.cppreference.com/w/cpp/algorithm/count

执行所需操作的最简单方法是对数组进行排序,然后查看哪些元素被满足多次。如果不想修改数组本身,则必须创建一个副本。这是一个O(n*lg n)解决方案,如果你不关心订单,就没有额外的空间,如果你关心订单,就会有O(n)额外的空间。
sort(test.begin(), test.end());
// If you only care if there is a repeated element, do this:
int size = test.size();
unique(test.begin(), test.end());
if (test.size() != size) {
  cout << "An element is repeated.";
}
// If you do care which elements are repeated, do this:
for (unsigned index = 1; index < test.size(); ++index) {
  if (test[index] == test[index - 1] && (index == 1 || test[index - 2] != test[index])) {
     cout << test[index] << " is repeated.";
  }
}

我提供了两种解决方案:第一种是只关心字符串是否重复,第二种是关心哪些字符串重复。

如果您不介意额外的空间,请尝试将元素推入map。只要您发现元素已经在地图中,就可以直接发出错误信号。

map<string, int> occurrences;
for (vector<string>::const_iterator cit = test.begin(); cit != test.end(); ++cit)
    if ((++occurrences[*cit]) == 2)
        cout << "ERROR"; // You can even signal which element is repeated here easily, using *cit.

请注意,根据Tony Delroy的巧妙修正,该代码对每个重复的项目只正确地发出一次消息(即使该项目重复了很多次)。尽管这种方式正确地统计了整个集合中每个字符串的出现次数(这可能是必需的),但如果同一元素(或多个)有2个31副本,这种方式会导致int溢出。如果是这种情况,并且您确实需要每个字符串的计数,则可以使用long long int

如果你对每个字符串的计数不感兴趣,一种更有效的方法是使用set,正如smerlin所建议的那样(因为它只维护字符串,而不是像map那样维护一对字符串和int),从而减少空间需求。。。并且每当您在集合中找到项目时都会发出错误消息:

set<string> occurrences;
for (vector<string>::const_iterator cit = test.begin(); cit != test.end(); ++cit)
    if (false == occurrences.insert(*cit).second)
        cout << "ERROR"; // You can even signal which element is repeated here easily, using *cit.

如果您想在问题发生之前消除它,请将元素插入到set中。它会自动删除重复项。但是要注意set中的元素是经过排序的,因此不会保留插入顺序。如果你不介意的话,set要好得多,因为搜索它和按排序顺序读取元素要高效得多。

一个解决方案可以使用两个for循环。。。。我想这会很简单。。

例如:

std::vector<std::string> test;
test.push_back("YES");
test.push_back("YES");
for(int i = 0; i < test.size(); i++)
{
    for(int j = 0; j < test.size(); j++)
    {
         if(i != j)
         {
              if(test[i] == test[j])
              {
                   DCS_LOG_DEBUG("ERROR WITH COUNT")
              }
         }
    }
}