2017-02-15 15 views
-2

Я пытаюсь написать программу, которая будет принимать список строк в качестве входных данных и создавать хеш-таблицу с именем строки и ее позицией.C++ unordered_map insert в вектор

Пример:
векторные слова {"первый", "второй", "третий", "четвертый", "второй"};

выход:
первая 1
второй 2,5
третий 3
вперед 4

я столкнулся с двумя проблемами, пожалуйста, найти их в коде комментарии ниже.
Пожалуйста, скажите мне, что я делаю неправильно?

int main() 
{ 
    vector<string> words {"first", "second", "third", "forth", "second"}; 

    unordered_map<string, vector<int>> hash_table; 
    unordered_map<string, vector<int>>::const_iterator hash_it; 

    int loc = 1; 

    for(auto n = words.begin(); n != words.end(); ++n){ 
     hash_it = hash_table.find(*n); 
     if(hash_it == hash_table.end()) 
      hash_table.insert(make_pair(*n, vector<int> (loc))); 
     else 
      //hash_it->second.push_back(loc); //Problem 1 - this statement gives error 

     ++loc; 
    } 

    for(auto& n:hash_table){ 
     cout<<"Word - "<<n.first<<" Loc -"; 
     vector<int> tmp1 = n.second; 
     for(auto j = tmp1.begin(); j != tmp1.end(); ++j) 
      cout<<" "<<*j; 
     cout<<endl; 
    } 
} 

Проблема 2 - значения местоположения равны 0
Выход программы -
Слово - вперед Лок - 0
Слово - третий Лок - 0
Слово - второй Лок - 0
Слово - первый Лок - 0

+1

Добро пожаловать в Stack Overflow. Пожалуйста, найдите время, чтобы прочитать [The Tour] (http: // stackoverflow.com/tour) и обратитесь к материалу из [Справочного центра] (http://stackoverflow.com/help/asking) о том, что и как вы можете задать здесь. –

+1

Какая ошибка точно была произведена, компиляция не удалась? –

+0

@ABusyProgrammer Ошибка была - В функции 'int main()': 23:42: error: передача 'const std :: vector ' как 'this' аргумент 'void std :: vector <_Tp, _Alloc> :: push_back (const value_type &) [с _Tp = int; _Alloc = std :: распределитель ; std :: vector <_Tp, _Alloc> :: value_type = int] 'отбрасывает квалификаторы [-fpermissive] – cplusplusnoob

ответ

3

вы усложненный вопрос, operator[] на карте или unordered_map созданы специально для таких случаев:

int loc = 1; 
for(auto n = words.begin(); n != words.end(); ++n) 
    hash_table[*n].push_back(loc++); 

Это код, который вам нужен. Вы можете сделать это еще проще, используя для цикла диапазон:

int loc = 1; 
for(const auto &word: words) 
    hash_table[word].push_back(loc++); 
+0

Большое спасибо Slava, это экономит массу усилий и довольно легко, теперь код работает отлично. – cplusplusnoob

3

Первая проблема заключается в использовании const_iterator, где вы должны использовать iterator. Вы не можете изменить элемент, на который ссылается const_iterator.

Использовать unordered_map<string, vector<int>>::iterator hash_it; вместо unordered_map<string, vector<int>>::const_iterator hash_it;. Еще лучше, используйте auto, чтобы автоматически вывести используемый тип.

for (auto n = words.begin(); n != words.end(); ++n) { 
    auto hash_it = hash_table.find(*n); 
// ^^^^ Deduce the correct type 
    if (hash_it == hash_table.end()) 
     hash_table.insert(make_pair(*n, vector<int>(loc))); 
    else 
     hash_it->second.push_back(loc); //No problem 

     ++loc; 
} 

Вторая проблема заключается в том, что оператор vector<int>(loc) делает вектор, содержащий loc значения, а не вектор, содержащий только loc. Самое простое изменение заключается в том, чтобы вместо этого использовать vector<int>(1, loc), что делает 1 значение равным loc.

for (auto n = words.begin(); n != words.end(); ++n) { 
    auto hash_it = hash_table.find(*n); 
// ^^^^ Deduce the correct type 
    if (hash_it == hash_table.end()) 
     hash_table.insert(make_pair(*n, vector<int>(1, loc))); 
    else 
     hash_it->second.push_back(loc); //Problem 1 - this statement gives error 

    ++loc; 
} 

Как уже отмечалось, вы можете сэкономить много неприятностей, используя operator[]. См. this answer от Slava для более простого способа сделать это.

+0

Большое спасибо Франсуа. Я не понял его константный итератор, теперь я понимаю, почему эта строка не работает. Это отличная помощь! – cplusplusnoob

0

Вы можете сделать код более эффективным, если используете unordered_multimap. Тогда вам не нужен вектор для позиций. Это работает, потому что элементы, имеющие один и тот же ключ, гарантированы в последовательном порядке.

int main() 
{ 
    vector<string> words {"first", "second", "third", "forth", "second"}; 

    unordered_multimap< string, size_t > hash_table; 

    size_t loc = 0; 
    for(const auto& word : words) 
    { 
     hash_table.insert(make_pair(word, ++loc)); 
    } 

    for(auto i = hash_table.begin(), j = hash_table.begin(); 
     i != hash_table.end(); 
     i = j) 
    { 
     cout << "Word - "<< i->first << " Loc -"; 

     // Iterate over all elements with same key 
     do 
     { 
      cout << " " << j->second; 
      ++j; 
     } 
     while(j != hash_table.end() && j->first == i->first); 

     cout << endl; 
    } 

    return 0; 
} 

Если вы хотите, чтобы посмотреть местоположение конкретного слова, вы будете использовать unordered_map :: equal_range(), как это:

cout << "Locations of 'second':"; 
auto rng = hash_table.equal_range("second"); 
for(auto i = rng.first; i != rng.second; ++i) 
    cout << " " << i->second; 
+0

, пожалуйста, простите мою нехватку знаний, но что делает size_t? хранит ли он какую-либо информацию? – cplusplusnoob

+0

size_t обычно определяется как целочисленный тип без знака, но он зависит от платформы. Когда вы компилируете для 64-битного файла, size_t обычно будет также 64-битным, а целое число (по крайней мере, под Windows) все равно будет 32-битным и, следовательно, недостаточно для хранения индекса вектора без переполнения. Именно по этой причине этот тип данных используется всеми стандартными контейнерами как тип возврата метода size(), например. – zett42

+0

Я исправил свой пример, чтобы использовать size_t как тип данных переменной loc. С 5 элементами вектора слов это, конечно, не важно, но рассмотрим очень большой вектор с более чем 2^31 словами ... в этом случае «int loc» уже может переполняться. – zett42