【问题标题】:Use of Vectors of vectors in C++ & push_back( )在 C++ 和 push_back( ) 中使用向量的向量
【发布时间】:2016-11-16 21:28:16
【问题描述】:

我是 C++ 新手,可能在这里遗漏了一些非常基本的东西,但我正在尝试创建一个向量向量

#include <iostream>
#include <stack>
#include <string>
#include <map>
#include <vector>
#include <algorithm>


using namespace std;

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs)
    {
        vector<vector<string>> result;
        map<string,vector<string>> myMap;

        if(strs.size() == 0)
        {
            return result;
        }

        for(string s : strs)
        {
            string temp = s;
            sort(temp.begin(),temp.end());
            auto it = myMap.find(temp);
            if(it != myMap.end())
            {
                it->second.push_back(s);
            }
            else
            {
                vector<string> newVector;
                newVector.push_back(s);
                myMap.insert(pair<string,vector<string>>(temp,newVector));
                result.push_back(newVector);
            }
        }
        cout<< myMap["abt"].size() <<endl;
        return result;
    }
};


int main(int argc, const char * argv[])
{
    Solution mySolution;
    vector<string> myStrings {"eat", "tea", "tan", "ate", "nat", "bat"};
    auto result = mySolution.groupAnagrams(myStrings);

    for(vector<string> v: result)
    {
        //cout << v.size() << endl;
        for(string s: v)
        {
            cout << s << " ";
        }
        cout << endl;
    }
    return 0;
}

我期待这样的输出

[
  ["ate", "eat","tea"],
  ["nat","tan"],
  ["bat"]
]

当我尝试在 main() 中打印向量的向量时,我得到所有向量的大小为 1。

好吧,当我打印地图中矢量的大小时,我觉得那里的大小还可以。我在这里错过了什么?

更新 -

通过以下更改修复它

for(string s : strs)
{
    string temp = s;
    sort(temp.begin(),temp.end());
    auto it = myMap.find(temp);
    if(it != myMap.end())
    {
        it->second.push_back(s);
    }
    else
    {
        vector<string> newVector;
        newVector.push_back(s);
        myMap.insert(pair<string,vector<string>>(temp,newVector));
    }
}
for(auto it: myMap)
{
    result.push_back(it.second);
}

我仍然想知道是否有办法最终避免循环遍历地图并实现我最初打算做的事情?

【问题讨论】:

  • 请注意for(string s : strs) 会复制strs 中的每个字符串。
  • "...如果有办法避免最终循环遍历地图" - 我不想仅仅因为在那个时候很容易出错(非常错误)进入vector&lt; vector&lt;string&gt; &amp; &gt; 领域。即使对于我自己的用途,我也会尽量避免传递引用类型的容器,因为如果您同时生成值只是为了将它们交给“外部”范围,那么它是内存管理不良的根源。您也可以使用指针,这很危险。

标签: c++ vector


【解决方案1】:

就是这个部分:

{
    vector<string> newVector;
    newVector.push_back(s);
    myMap.insert(pair<string,vector<string>>(temp,newVector));
    result.push_back(newVector);
}

每次result 都会被赋予一个包含一个元素的新向量。更改地图矢量而不是矢量矢量的原因是vector::push_back 每次都会创建一个副本。


解决这个问题有两种方法。

  1. 您可以尝试在更新地图的同时更新结果,并获取矢量以存储对地图副本的一些引用。
  2. 由于您没有将result 用于处理步骤,仅用于结果,您可以在完成地图后编译矢量。

我更喜欢方法#2,因为你永远不会返回地图本身。此外,从一种容器类型转换为另一种容器类型是一门艺术,例如,this question 提供了一些关于所涉及内容的想法。

【讨论】:

  • 导致问题的部分是 it-&gt;second.push_back(s) 线只推入地图,而不是 result 向量,因为它们是独立的东西(即使它们都是从 newVector 复制的)
  • 所以你是说存储在地图中的向量和存储在结果中的向量不同?并且所有更新都在存储在地图中的向量上进行,而不是在结果中的向量上进行,因为它们不同?
  • @ghanta_engineer 完全正确,是的。您的地图正在更新,您正在直接修改存储在地图中的矢量。然而,结果只需要一个填充了一个元素的副本,并且再也不会被引用。
  • 我明白了,那么我如何不制作不同的副本并确保它指向我正在更新的那个向量?
  • @TheDark 同意不同意 - 问题是有两个向量实例在起作用,我们正在更新 A 但返回 B。我个人认为问题在于结果,因为地图首先用于数据处理。
【解决方案2】:

问题出在这部分代码上:

vector<string> newVector;
newVector.push_back(s);
myMap.insert(pair<string,vector<string>>(temp,newVector));
result.push_back(newVector);

分解:

vector<string> newVector;

创建一个新的本地临时向量。

newVector.push_back(s);

newVector 后面为string 分配空间,并将s 复制到其中。

myMap.insert(pair<string,vector<string>>(temp,newVector));

创建一个包含 temp 副本和 newVector 副本的 std::pair - 原样, 然后为映射中的匹配对分配空间并将临时对(即再次复制字符串和向量)复制到其中。

result.push_back(newVector);

这会为结果后面的新向量分配空间,并将newVector复制到其中。

resultmyMap 此时包含 newVector 的独立快照。您的其余代码更新了myMap 中的向量,但结果保持不变。

您可以通过不即时构建 result 来解决此问题,而仅在完成所有工作后构建它:

    // take a reference to each string in the vector,
    // const indicates it will be immutable
    for(const string& s : strs)
    {
        string temp = s;
        sort(temp.begin(),temp.end());
        std::vector<string>& dest = myMap[temp];
        dest.emplace_back(s);
    }

    cout<< myMap["abt"].size() <<endl;

    for (auto& kv : myMap)  // kv: key value
    {
        // std::move tells 'emplace_back' it can steal the
        // vector's data right out of kv.second.
        result.emplace_back(std::move(kv.second));
    }

生成的代码在这里演示:http://ideone.com/eofloM

#include <iostream>
#include <stack>
#include <string>
#include <map>
#include <vector>
#include <algorithm>


using namespace std;

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs)
    {
        vector<vector<string>> result;
        map<string,vector<string>> myMap;

        if(strs.size() == 0)
        {
            return result;
        }

        for(const string& s : strs)
        {
            string temp = s;
            sort(temp.begin(),temp.end());
            std::vector<string>& dest = myMap[temp];
            dest.emplace_back(s);
        }

        cout<< myMap["abt"].size() <<endl;

        for (auto& kv : myMap)  // kv: key value
        {
            result.emplace_back(std::move(kv.second));
        }

        return result;
    }
};


int main(int argc, const char * argv[])
{
    Solution mySolution;
    vector<string> myStrings {"eat", "tea", "tan", "ate", "nat", "bat"};
    auto result = mySolution.groupAnagrams(myStrings);

    for(vector<string> v: result)
    {
        //cout << v.size() << endl;
        for(string s: v)
        {
            cout << s << " ";
        }
        cout << endl;
    }
    return 0;
}       

【讨论】:

    【解决方案3】:

    我仍然想知道是否有办法最终避免循环遍历地图并实现我最初打算做的事情?

    class Solution {
    
    private:
            vector<vector<string>*> result;
            map<string,vector<string>> myMap;
    
    public:
        vector<vector<string>*> groupAnagrams(vector<string>& strs)
        {
    //        vector<vector<string>*> result;
    //        map<string,vector<string>> myMap;
    

    只需生成结果并映射类的成员。看向量指针的类型改为向量。

            else
            {
                vector<string> newVector;
                newVector.push_back(s);
                auto p = myMap.insert(pair<string,vector<string>>(temp,newVector));
    
                //p is pair<iterator, bool>
                //where iterator is pointing to the inserted element
                //so p.first->second is the new vector
    
                result.push_back(&(p.first->second));
            }
    

    注意这里我们将向量的地址放在地图中。

    for(vector<string>* v: result)
    {
        for(string s: *v)
        {
            cout << s << " ";
        }
        cout << endl;
    }
    

    迭代向量并考虑指针类型给出的结果为:

    eat tea ate 
    tan nat 
    bat
    

    这样就去掉了第二个循环,占用的空间更少,但是代码有点复杂。

    【讨论】:

      猜你喜欢
      • 2020-11-20
      • 2013-03-28
      • 1970-01-01
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-09
      • 1970-01-01
      相关资源
      最近更新 更多