MAP oraz SET


Wiadomości ogólne

ZAGADNIENIA

mapa

Oto zestaw zagadnień, które mogą Ci pomóc poznać struktury danych std::map i std::set w C++:
Podstawowe operacje:
  • Stwórz pusty zbiór i mapę.
  • Wstaw kilka elementów do każdej struktury.
  • Sprawdź, czy dany element jest obecny w zbiorze/mapie.
  • Usuń kilka elementów z każdej struktury.
  • Iteracja:
  • Przejdź przez wszystkie elementy zbioru.
  • Przejdź przez wszystkie pary klucz-wartość w mapie.
  • Sortowanie:
  • Wstaw losowe liczby do zbioru, a następnie wyświetl je w kolejności rosnącej.
  • Wstaw pary klucz-wartość do mapy z losowymi kluczami, a następnie wyświetl je w porządku kluczy.
  • Wyszukiwanie:
  • Spróbuj wyszukać element, który jest w zbiorze/mapie.
  • Spróbuj wyszukać element, który nie jest w zbiorze/mapie.
  • Multizbiór i multimapa:
  • Zastanów się, w jaki sposób można zaimplementować własny multizbiór (zbiór z duplikatami) i multimapa (mapa z duplikatami).
  • Napisz funkcję, która zlicza wystąpienia każdego elementu w kolekcji i zwraca wynik jako multimapa.
  • Zaawansowane operacje:
  • Wypróbuj operacje na zbiorach, takie jak unia, przecięcie i różnica.
  • Zaimplementuj prosty algorytm, który usuwa duplikaty z kolekcji za pomocą zbioru.
  • Złożoność czasowa:
  • Porównaj czas wykonania różnych operacji na zbiorze i mapie dla różnych rozmiarów danych wejściowych.
  • Zbadaj, jak złożoność czasowa zmienia się wraz z rozmiarem danych wejściowych.
  • Zastosowania praktyczne:
  • Rozwiąż kilka problemów praktycznych, takich jak zliczanie słów w tekście za pomocą mapy, usuwanie duplikatów z listy za pomocą zbioru, czy znajdowanie wspólnych elementów między dwoma zbiorami.
  • Testowanie i debugowanie:
  • Przetestuj swoje rozwiązania na różnych zestawach danych wejściowych.
  • Debuguj problemy związane z operacjami na zbiorze/mapie, takie jak błędy przy wstawianiu, usuwaniu lub wyszukiwaniu elementów.
  • Optymalizacja wydajności:
  • Zastanów się, w jaki sposób można zoptymalizować kod korzystający z mapy/zbioru, aby był bardziej wydajny.
  • Przetestuj różne podejścia i porównaj ich wydajność.

    Struktura danych std::set w C++.

    std::set jest częścią biblioteki standardowej języka C++ i reprezentuje kolekcję unikalnych elementów, posortowanych zgodnie z określonym porządkiem.

    Możesz użyć std::set, aby przechowywać dane w porządku rosnącym lub malejącym i szybko sprawdzać, czy określony element znajduje się w zbiorze.

    Typowe zastosowania SET


    W C++ instrukcje związane z std::set są zwykle funkcjami i metodami, które pozwalają na manipulację i interakcję ze zbiorem. Oto kilka najczęściej używanych:

  • insert(): Dodaje nowy element do zbioru.
    std::set mySet;
    mySet.insert(5);

  • erase(): Usuwa element ze zbioru.
  • std::set mySet = {1, 2, 3, 4, 5};
    mySet.erase(3);

  • find(): Znajduje określony element w zbiorze i zwraca iterator do niego, lub zwraca end() jeśli element nie został znaleziony.
  • std::set mySet = {1, 2, 3, 4, 5};
    auto it = mySet.find(3);
    if (it != mySet.end()) {
    std::cout << "Element znaleziony: " << *it << std::endl;
    }

  • size(): Zwraca liczbę elementów w zbiorze.
  • set mySet = {1, 2, 3, 4, 5};
    cout <<"Liczba elementów w zbiorze: " << mySet.size();

  • clear(): Usuwa wszystkie elementy ze zbioru.
  • set mySet = {1, 2, 3, 4, 5};
    mySet.clear();

  • begin() i end(): Zwracają iteratory na początek i koniec zbioru.
  • set mySet = {1, 2, 3, 4, 5};
    for (auto it = mySet.begin();
    it != mySet.end(); ++it) {
    cout << *it << endl;
    }

    To tylko kilka przykładów funkcji i metod związanych z std::set. Istnieje wiele innych, w tym te związane z operacjami na zbiorach, takimi jak unia, przecięcie czy różnica(dla bardzo chętnych).

    std::map jest kolejnym kontenerem w bibliotece standardowej C++, który implementuje strukturę danych zwanej mapą lub słownikiem. Jest to kolekcja par klucz-wartość, gdzie klucze są unikalne i posortowane, a wartości są powiązane z nimi. W kontenerze std::map każdy klucz może być przypisany do jednej wartości.

    Oto kilka cech std::map:

    Unikalność kluczy: Każdy klucz w mapie jest unikalny. Nie może istnieć dwa identyczne klucze w jednej mapie.

    Sortowanie: Elementy mapy są automatycznie sortowane względem kluczy zgodnie z określonym porządkiem. Domyślnie porządek ten jest porządkiem rosnącym.

    Efektywne wyszukiwanie: Wyszukiwanie elementów w mapie jest bardzo efektywne, ponieważ jest to implementowane w oparciu o drzewo poszukiwań binarnych lub równoważną strukturę danych.

    Dynamiczna struktura: Mapa automatycznie dostosowuje swoją strukturę do zmian w liczbie elementów, co oznacza, że ​​możesz dodawać i usuwać elementy dynamicznie.

    Przykład załóżmy że mamy takie liczby: 3, 5, 8, 6, 3, 2, 1, 3, 5, 7, 4, czy można za pomocą set zliczyć ile jest jakich elementów?

    Rozwiązanie Tak, możemy użyć kontenera std::set, aby zliczyć, ile razy każdy element występuje w zestawie. Jednak sama klasa std::set przechowuje tylko unikalne elementy, więc w przypadku, gdy potrzebujesz zliczyć wystąpienia każdego elementu, możesz użyć std::multiset, który pozwala przechowywać duplikaty. Oto jak to zrobić:

    #include <iostream>
    #include <set>
    using namespace std;
    int main() {
    multiset<int> mySet = {3, 5, 8, 6, 3, 2, 1, 3, 5, 7, 4};
    // Mapa do przechowywania liczby wystąpień każdego elementu
    std::map<int, int> occurrences;
    //Iterujemy przez zbiór i zliczamy wystąpienia każdego elementu
    for (int num : mySet) {
    occurrences[num]++;
    }
    // Wyświetlamy wyniki
    for (const auto& pair : occurrences) {
    cout << "Liczba " << pair.first << " występuje " << pair.second << " razy." << endl;
    }
    return 0;
    }

    W tym kodzie używamy std::multiset do przechowywania wszystkich liczb, a następnie iterujemy po zestawie, korzystając z mapy occurrences, aby zliczyć wystąpienia każdej liczby. Na końcu wypisujemy liczbę wystąpień każdego elementu.

    Przykład
    Dany jest plik tekstowy zawierająy np.: liczby całkowite. Wczytaj te liczby to struktury set a następnie wyświetl

    Rozwiązanie
    Aby wczytać liczby z pliku tekstowego do zbioru (set) w C++, możesz użyć strumieni wejściowych ifstream oraz operacji na zbiorze. Oto przykładowy kod realizujący to zadanie:

    #include <iostream>
    #include <fstream>
    #include <set>
    using namespace std;
    int main() {
    ifstream plik("nazwa_pliku.txt"); // Otwieramy plik do odczytu
    set<int> zbior;
    int liczba;
    while (plik >> liczba) { // Odczytujemy liczby z pliku
    zbior.insert(liczba); // Wstawiamy odczytaną liczbę do zbioru
    }
    plik.close(); // Zamykamy plik
    // Wyświetlamy zawartość zbioru
    cout << "Liczby wczytane do zbioru:" << endl;
    for (const auto& element : zbior) {
    cout << element << endl;
    }
    return 0;
    }

    Ten kod otwiera plik o nazwie "nazwa_pliku.txt" i wczytuje kolejne liczby ze strumienia do zbioru. Następnie wyświetla zawartość zbioru. Upewnij się, że plik "nazwa_pliku.txt" zawiera jedną liczbę na linię, aby ten kod działał poprawnie.

    Linia for (const auto& element : zbior) to tzw. pętla zakresowa (ang. range-based for loop) w C++. Pozwala ona iterować przez wszystkie elementy w kontenerze, takim jak zbiór (std::set), bez potrzeby korzystania z iteratorów w sposób jawny.
    Termin const auto& element oznacza, że dla każdego elementu w zbiorze zbior, typ elementu będzie automatycznie wydedukowany przez kompilator (dzięki auto), a następnie użyty do zadeklarowania zmiennej element.
    const oznacza, że element będzie traktowany jako stała, więc nie możemy jej modyfikować.
    auto oznacza, że typ element będzie automatycznie dedukowany przez kompilator na podstawie typu elementów w zbiorze.
    & oznacza, że element będzie przekazywany przez referencję, co pozwala uniknąć kopiowania elementów.
    Wewnątrz pętli, dla każdego elementu w zbiorze, kod wykonuje się z użyciem zmiennej element, która reprezentuje aktualnie przetwarzany element zbioru.
    Na przykład, w kontekście kodu wczytującego liczby z pliku do zbioru, linia ta iteruje przez każdą liczbę wczytaną do zbioru i wyświetla ją na ekranie.

    Ćwiczenia

    Wczytaj przykład:

    #include <iostream>
    #include <unordered_set>
    #include <unordered_map>
    #include <utility>
    
    using namespace std;
    
    int main()
    {
       unordered_multiset<string> names;
       names.insert("Romeo");
       names.insert("Julia");
       names.insert("Romeo"); // Funkcja names.count("Romeo") zwraca teraz liczbę 2
       cout << "Liczba Romeow: " << names.count("Romeo") << endl;
       cout << "Liczba Julii: " << names.count("Julia") << endl;
       names.erase("Julia"); // Funkcja names.count("Julia") zwraca teraz liczbę 0
       cout << "Liczba Julii: " << names.count("Julia") << endl;
       names.erase("Julia");
          // Nie ma żadnego skutku: w wielozbiorze nie ma już ciągu "Julia"
       cout << "Liczba Julii: " << names.count("Julia") << endl;
    
       unordered_multimap<string, string> friends;
       friends.insert(make_pair("Tomasz", "Daria")); // Daria przyjaźni się z Tomaszem.
       friends.insert(make_pair("Tomasz", "Henryk")); // Henryk też się z nim przyjaźni.
    
       auto tom_range = friends.equal_range("Tomasz");
          // pair::iterator>
       cout << "Przyjaciele Tomasza: ";
       for (auto pos = tom_range.first; pos != tom_range.second; pos++)
       {
          cout << pos->second << " ";
       }
       cout << endl;
    
       bool found = false;
       for (auto pos = tom_range.first; !found && pos != tom_range.second; pos++)
       {
          if (pos->second == "Daria")
          {
             found = true;
             friends.erase(pos);
             cout << "Daria nie przyjazni sie juz z Tomaszem" << endl;
          }
       }
    
       cout << "Liczba pozostalych przyjaciol Tomasza: "
          << friends.count("Tomasz") << endl;
    
       return 0;
    }
    

    Operacje na zbiorach teoria, przykład przykład

    ZAGADNIENIA

  • Tworzenie sumy zbiorów za pomocą funkcji set_union()
  • Tworzenie części wspólnej zbiorów za pomocą funkcji set_intersection()
  • Tworzenie różnicy zbiorów za pomocą funkcji set_difference()
  • Tworzenie różnicy symetrycznej zbiorów za pomocą funkcji set_symmetric_difference()
  • Wykrywanie podzbiorów
    Załóżmy, że mamy dwa zbiory, z których jeden zawiera wszystkie elementy drugiego, na przykład zbiór set1 zawiera wartości 1, 2, 3, 4, a set2 — wartości 2, 3. W tym przypadku zestaw set1 zawiera wszystkie elementy zbioru set2, co oznacza, że set2 jest podzbiorem zbioru set1.
    Aby sprawdzić, czy jeden zbiór jest podzbiorem innego, można użyć funkcji includes().
    Jej argumentami są dwa iteratory wskazujące posortowane zakresy elementów. Funkcja zwraca wartość true, jeżeli pierwszy zakres zawiera wszystkie elementy drugiego, lub false w przeciwnym razie.
    Użycie funkcji includes() demonstruje poniższy przykład:
    Program sprawdza, czy zbiór set1 zawiera wszystkie elementy zbioru set2.
    
    #include <iostream>
    #include <set>
    #include <algorithm>
    using namespace std;
    
    int main()
     {
     // Utworzenie dwóch zbiorów
     set<int> set1 = { 1, 2, 3, 4 };
     set<int> set2 = { 2, 3 };
    
     // Sprawdzenie, czy zbiór set1 zawiera wszystkie elementy zbioru set2
     if (includes(set1.begin(), set1.end(),  set2.begin(), set2.end()))
     {
    	cout << "Zbior set2 jest podzbiorem zbioru set1.\n";
     }
     else
     {
    	cout << "Zbior set2 NIE jest podzbiorem zbioru set1.\n";
     }
    
    	return 0;
    }
    
    
    	
    	


    Operacje na zbiorach - przykład

    Poniższy przykład demonstruje różne operacje na zbiorach. Program tworzy dwa zbiory: football, zawierający imiona zawodników drużyny piłki nożnej, i basketball z imionami zawodników drużyny koszykarskiej.
    Program wykonuje następujące operacje:
  • Tworzy część wspólną obu zbiorów i wyświetla imiona zawodników uprawiających obie dyscypliny.
  • Tworzy sumę zbiorów i wyświetla imiona zawodników uprawiających przynajmniej jedną z dyscyplin.
  • Tworzy różnicę zbiorów football - basketball, a następnie wyświetla imiona zawodników uprawiających piłkę nożną, ale nie koszykówkę.
  • Tworzy różnicę zbiorów basketball - football, a następnie wyświetla imiona zawodników grających w koszykówkę, ale nie piłkę nożną.
  • Tworzy różnicę symetryczną zbiorów football i basketball, a następnie wyświetla imiona zawodników uprawiających tylko jedną dyscyplinę.

    Link do pliku - programu:
    pobierz program

  • Sprawdź się

    SPRAWDŹ SIĘ

    1. Unikatowe słowa
      Napisz program otwierający plik tekstowy i wyświetlający unikatowe słowa użyte w tym pliku.

    2. Quiz geograficzny
      Napisz program tworzący mapę, w której kluczami będą nazwy europejskich państw, a wartościami — ich stolice (lista państw i ich stolic znajduje się w załączonym pliku Stolice ). Program powinien wybierać losowo nazwę państwa i prosić użytkownika o podanie nazwy jego stolicy.
      Program powinien także zliczać poprawne i błędne odpowiedzi.

    3. Szyfrowanie i deszyfrowanie pliku
      Napisz program wykorzystujący mapę, w której każdej literze alfabetu będzie przypisany określony „kod”, na przykład:
      map codes = { {'A', '%'}, {'a', '9'}, {'B', '@'}, {'b', '#'}, itd...};
      W powyższym przykładzie literze „A” jest przypisany symbol %, literze „a” — cyfra 9, literze „B” — symbol @ itd. Program powinien otwierać zadany plik, odczytywać jego zawartość, następnie ją szyfrować i zapisywać w drugim pliku. Każdy znak w drugim pliku musi zawierać kod odpowiadający znakowi z pierwszego pliku. Napisz drugi program, który będzie otwierał zaszyfrowany plik, deszyfrował jego zawartość i wyświetlał ją na ekranie

    4. Analiza pliku tekstowego
      Napisz program, który odczytuje zawartość dwóch plików tekstowych i porównuje je w następujący sposób:
      a/ Wyświetla listę wszystkich unikatowych słów występujących w obu plikach.
      b/ Wyświetla listę słów występujących przynajmniej w jednym pliku.
      c/ Wyświetla listę słów występujących w pierwszym pliku, których nie ma w drugim.
      d/ Wyświetla listę słów występujących w drugim pliku, których nie ma w pierwszym.
      e/ Wyświetla listę słów występujących w pierwszym i drugim pliku, z wyjątkiem tych, które występują w obu plikach jednocześnie.
      Wskazówka: do wykonania powyższej analizy wykorzystaj metody klasy set

    5. Częstość słów Napisz program odczytujący zawartość pliku tekstowego. Program powinien tworzyć mapę, w której kluczami będą unikatowe słowa użyte w pliku, a wartościami — liczby ich wystąpień. Jeżeli na przykład słowo „jest” występuje w pliku 128 razy, mapa powinna zawierać element z kluczem „jest” i wartością 128. Program powinien wyświetlać na ekranie lub zapisywać w drugim pliku listy słów i liczby ich wystąpień.

    6. Indeks słów Napisz program odczytujący zawartość pliku tekstowego. Program powinien tworzyć mapę zawierającą następujące pary klucz-wartość:

       klucz: unikatowe słowo użyte w pliku;
       wartość: zbiór zawierający numery wierszy, w których występuje dane słowo(klucz).

      Załóżmy, że słowo „robot” występuje w wierszach 7., 18., 94. i 138.
      Niech mapa zawiera element, w którym kluczem jest słowo „robot”, a wartością — zbiór liczb 7, 18, 94 i 138.
      Program po utworzeniu mapy powinien tworzyć drugi plik tekstowy z indeksem słów. W pliku tym ma zapisywać w porządku alfabetycznym słowa użyte w oryginalnym pliku wraz z numerami wierszy, w których zostały zastosowane.

    7. Ceny paliwa Do poniższego zadania pobierz plik tekstowy paliwo.txt zawierający średnie tygodniowe hurtowe ceny paliwa od 3 stycznia 2004 r. do 4 sierpnia 2018 r.
      Zawartość pliku Paliwo.txt:
      Każdy wiersz pliku zawiera średnią cenę jednego litra paliwa w określonym dniu, zapisaną w następującym formacie:
      RRRR-MM-DD:Cena
      gdzie RRRR oznacza rok, MM — miesiąc, DD — dzień, a Cena oznacza średnią hurtową cenę jednego litra paliwa w danym dniu.
      Napisz jeden lub kilka programów odczytujących zawartość powyższego pliku i wyliczających następujące dane:
      Średnia cena roczna: program powinien wyliczać średnią cenę paliwa za każdy rok. (Dane w pliku obejmują okres od stycznia 2004 r. do sierpnia 2018 r.
      Średnia cena miesięczna: program powinien wyliczać średnią cenę paliwa za każdy miesiąc.
      Najwyższa i najniższa cena roczna: program powinien wyszukiwać najwyższe i najniższe ceny w każdym roku i odpowiadające im daty.
      Lista cen od najniższej do najwyższej: program powinien tworzyć plik tekstowy zawierający listę dat i cen posortowanych od najniższej do najwyższej.
      Lista cen od najwyższej do najniższej: program powinien tworzyć plik tekstowy zawierający listę dat i cen posortowanych od najwyższej do najniższej.
      Możesz napisać jeden program wykonujący wszystkie powyższe operacje lub osobne programy wykonujące poszczególne operacje. Niezależnie od wyboru program musi odczytywać zawartość pliku Paliwo.txt, wyodrębniać z niego dane i umieszczać je w jednym lub kilku kontenerach odpowiednich do wybranego algorytmu.



    Warsztat projektanta algorytmów






    Przykładowe rozwiązania oraz komentarze do wybranych ćwiczeń i zadań
  • Analiza do zadania 2 - Quiz geograficzny (na przykładzie imion) - pobierz tutaj