【问题标题】:Pushing unique data into vector将唯一数据推送到向量中
【发布时间】:2012-04-29 21:18:34
【问题描述】:

我有以下数据:

FolioA Name1 100
FolioA Name2 110
FolioA Name3 100
FolioB Name1 100
FolioB Name3 106
FolioC Name1 108
FolioC Name2 102
FolioC Name3 110

我只想将唯一名称(即 Name1、Name2 和 Name3,各一次)插入

std::vector<std::string> name;

当我遍历数据时。

所以,我有以下代码,我将数据存储在名为 test 的地图中:

std::map<std::string, std::map<std::string, double> >test;
std::map<std::string, std::map<std::string, double > >::iterator it1 = test.begin(), end1 = test.end();
    while (it1 !=end1) {
        std::map<std::string, double>::iterator it2 = it1->second.begin(), end2=it1->second.end();
        **name.push_back(it2->first);**
        ++it2;
    }
    ++it1;
}

但是,目前通过以我的方式将数据推送到名称中,有 3 个 Name1 实例、2 个 Name2 实例和 3 个 Name3 实例,这是我的代码所期望的。如何将其修复为只有唯一名称。

【问题讨论】:

  • 您将如何选择要包含的实例?
  • 你必须有一个vector 的名字吗?我建议为此使用set 的名称。如果您必须有一个向量,您仍然可以先插入一个集合,然后使用采用iterators 的构造函数重载将对象移动到vector
  • @juanchopanza,我会选择每个的第一个实例,所以如果向量已经包含(Name1,Name2,Name3),那么当它到达第 4 条记录时,它会识别该记录已经存在,所以它会跳过它,然后转到下一条记录。
  • @Chad,你能发布示例代码

标签: c++ insert unique stdvector


【解决方案1】:

由于您想保留给定名称的第一个实例,因此您必须在某个时候执行名称查找。一个只涉及你的向量的简单算法是可以使用std::find检查条目是否已经存在

std::vector<std::string> name;

....
if (std::find(name.begin(), name.end(), someName) == name.end()) {
  // someName not in name, add it
  name.push_back(someName);
}

但是在这里,您每次想要插入一个元素时都在执行搜索,这(本身)高达O(N) 复杂度,为整个算法提供O(N*N)。因此,您可以通过使用具有快速查找功能的中间容器进行优化,例如@Chad 建议的std::set,它具有O(logN) 的查找复杂性,总体上给出O(N*logN),或者像C 这样的哈希容器++11 的std::unordered_set,具有接近恒定时间查找,总体复杂度约为 O(N)。

#include <unordered_set>

std::unordered_set<std::string> name_set;
....

// still need to search, since you want to keep 
// the first instance of each name, and not the last.
// But unordered_set performs the look-up at insertion,
// only inserting if someName not already in the set
name_set.insert(someName);

然后,按照@Chad 的例子,

std::vector<std::string> name(names_set.begin(), name_set.end());

如果您没有 C++11,哈希映射的替代品是 boost::hash_maptr1::hash_map

【讨论】:

  • 啊哈,听起来好像可以。你能发布我将如何使用它的示例代码吗?
  • Schlemiel the painter's algorithm。 IE。 O(N*N)。它有效,但 Chad 的解决方案的扩展性要好得多。
  • @MSalters,也许您可​​以解释一下为什么要按您的方式对其进行分类。乍得的解决方案也有效,但上面的错误是什么。
  • @user1155299 问题是这个算法效率不高,即它的复杂性过高。我提供了一个更好的例子和一些解释。
  • @MSalters 非常正确。乍得的解决方案并没有解决实际问题,但非常接近。我已经用一些解释更新了我的答案。
【解决方案2】:

您要求提供示例代码,所以我会这样做:

std::set<std::string> unique_names;

// ...
while (it1 !=end1)
{
    // ...
    // **name.push_back(it2->first);**
    unique_names.insert(it2->first);
}

std::vector<std::string> name(unique_names.begin(), unique_names.end());

【讨论】:

    【解决方案3】:

    如果您不关心要将哪个实例输入数据结构,std::set 将满足您的目的

    【讨论】:

      【解决方案4】:

      也许您应该使用另一个地图而不是矢量来获得唯一的名称。

      std::map <:string double> 名称;

      【讨论】:

        【解决方案5】:

        list 具有 .sort() 和 .unique() 的能力,这将为您提供。

        您可以使用迭代器对其进行迭代,并使用 initializer_list 对其进行初始化。

        在我看来,数据实际上更像是一个结构:

        #include <iterator>
        #include <list>
        #include <string>
        #include <fstream>
        
        typedef struct NODE_S {
            string name1, name2;
            int n;
        } NODE_S NODE;
        
        bool compare_NODE (NODE first, NODE second)
        {
            unsigned int i=0;
            if (first.name1 < second.name1) {
                return true;
            } else if (first.name2 < second.name2) {
                return true;
            } else if (first.n < second.n) {
                return true;
            } else { return false;}
        }
        
        
        bool readfile(list<NODE>& ln, string filepath) {
            std::ifstream filein;
            NODE n;
            filein.open(filepath.c_str(), std::iofstream::in);
            if (!filein.good()) {
                filein.close();
                std::cerr << "ERROR: unable to open file \"" << filepath << "\" or file is zero-length." << std::endl;
                return false;
            }
            do {
                filein >> n.name1 >> n.name2 >> n.name3 >> std::skipws;
                ln.push_back(n);
                ln.sort(compare_NODE);
                ln.unique();
                //add node to list
        
            } while (!filein.good()); //can use .eof here, but if bad disk blocks...
            filein.close();
            return true;
        }
        
        
        int main(int argc, char * argv[], char * envp[]) {
            string filepath="somefile.txt";
            if (!readfile(filepath)) {
                return 1;
            }
            list<NODE>::iterator lni;
            for (lni = ln.begin(); lni != ln.end(); lni++) {
                std::cout<<lni->name1<<' '<<lni->name2<<' '<<lni->n<<std::endl;
            }
            return 0;
        }
        

        http://www.cplusplus.com/reference/stl/list/sort/

        http://www.cplusplus.com/reference/stl/list/unique/

        【讨论】:

        • 您需要将sortunique 调用向下移动到循环之外。现在每个NODE 都会调用一次。顺便说一句,typedef struct 的东西已经 20 年不需要了。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-18
        • 2020-06-22
        • 1970-01-01
        • 2020-05-11
        • 2022-01-14
        相关资源
        最近更新 更多