그냥 게임개발자

map 본문

내 개인적인 공부/자료구조

map

sudoju 2024. 4. 13. 21:36

map은 C#에서 Dictionary와 같습니다.

 

고유한 키를 가지며 그 키를 기반으로 key - value 쌍으로 이루어져 있는 정렬된 연관 컨테이너입니다.

 

균형 이진 검색트리(레드 - 블랙트리)로 구현이 되어있어서, 삽입, 삭제 수정 탐색 시간 복잡도는 O(logN)의 시간 복잡도를 가지게 됩니다.

 

고유한 키를 가지기 때문에 하나의 키에 중복된 값이 들어갈 수는 없습니다.

또한 자동으로 오름차순으로 정렬이 되기 때문에 넣은 순서대로 탐색이 아니라 ASCII 순서대로 정렬된 값들을 기반으로 탐색하게 됩니다.

 

참조할 때는 대괄호 연산자 [ ]로 해당 키를 넣어 직접 참조할 수 있습니다.

 

예를 들어 위에처럼 "The Invisible Man" : 4, "Helen Keller" : 7 이런식의 string : int 형태로 값을 할당해야 할 때 map을 씁니다.

 

맵의 키는 string, int, double 여러가지 다양한 타입이 들어갈 수 있습니다.

 

아래와 같이 map은 여러가지 초기화 방법이 있는데

#include <iostream>
#include <map>

using namespace std;

map<string, int> mp;

string str[] = { "C++", "C#", "C" };

int main()
{
    for (int i = 0; i < 3; ++i)
    {
    	// 여러가지 초기화 방법
        mp.insert({ str[i], i + 1 });
        mp.insert(pair<string, int>(str[i], i + 1));
        mp.insert(make_pair(str[i], i + 1));
        mp[str[i]] = i + 1;
    }
}

 

map은 key-value가 pair형태로 들어가는 것을 볼 수가 있습니다.

그렇다면 맨 아래 mp[str[i]] = i + 1;은 무엇일까요?

만약에 key가 있다면 value의 값을 바꿔주고 그게 아니라면 key를 생성하고 value로 초기화 하게 됩니다.

 

그러면 이런 것은 어떨까요?

 

#include <iostream>
#include <map>

using namespace std;

map<string, int> mp;

int main()
{
    cout << mp["Santos"] << '\n';
}

.

초기화도 안하고 바로 key값을 통해 value를 참조하면 어떠한 값이 나올까요?

네 0이 나오더라구요

그냥 기본값이 0으로 초기화가 됩니다

그렇다면 궁금한게 생겼어요.

#include <iostream>
#include <map>

using namespace std;

map<string, char> mp;

int main()
{
    cout << mp["Santos"] << '\n';
}

value의 타입이 char면 어떨까요?

아무것도 없네요.

당연하죠 0으로 초기화되었다라는건

ASCII코드에서 확인해보면

 

아저씨코드

 

NULL이니 당연히 빈 문자열이 나오는게 맞습니다.

 

더 찾아봐요 더햅봐요!!

#include <iostream>
#include <map>

using namespace std;

map<string, double> mp;

int main()
{
    cout << mp["Santos"] << '\n';
}

 

음 이로써 알게 되었네요.

key만 생성하고 value값을 정하지 않으면 0이라는 값으로 초기화가 되는군요!

 

좋아요!

그러면 한꺼번에 map의 기능들을 살펴보고 차근차근 설명해보죠

 

#include <iostream>
#include <map>

using namespace std;

int main()
{
    map<string, int> mp;
    
    string str[] = { "C++", "C#", "C" };
    
    for (int i = 0; i < 3; ++i)
    {
        mp.insert(pair<string, int>(str[i], i + 1));
    }
    
    // 키값이 없으니 0으로 초기화해서 출력
    cout << mp["Santos"] << '\n';
    
    // 초기화는 이미 했으니 value값 수정
    mp["Santos"] = 4;
    
    // 4
    cout << mp.size() << '\n';
    
    // Santos 삭제
    mp.erase("Santos");
    
    // auto => map<string, int>::iterator 
    auto findIt = mp.find("Santos");
    
    if (findIt == mp.end()
    {
        cout << "지웠으니 당연히 못찾음" << '\n';
    }
    
    // 초기화 동시에 value값 할당
    mp["Santos"] = 100;
    
    findIt = mp.find("Santos");
    
    // Santos : 100
    if (findIt != mp.end())
    {
        cout << (*it).first << " : " << (*it).second << '\n';
    }
    
    // C++ : 1
    // C# : 2
    // C : 3
    // Santos : 100
    // auto => 타입 map<string, int>::iterator::value_type
    for (auto it : mp)
    {
        cout << (it).first << " : " << (it).second << '\n';
    }
    
    // C++ : 1
    // C# : 2
    // C : 3
    // Santos : 100
    for (auto it = mp.begin(); it != mp.end(); ++it)
    {
        cout << (*it).first << " : " << (*it).second << '\n';
    }
    
    mp.clear();
    
    return 0;
}

 

어우 많다 많다.

 

결과 값을 확인하자.

 

insert({key, value})

map에다 pair 형태{key, value}를 집어 넣습니다.

 

[key] = value

대괄호 [ ]를 통해 key를 기반으로 map에 있는 요소를 참조한다.

cout << mp["Santos"] << '\n';

 

size()

map에 있는 요소들의 개수를 반환한다.

cout << mp.size() << '\n';

 

erase(key)

해당 키에 해당하는 요소를 지웁니다.

mp.erase("Santos");

 

find(key)

map에서 해당 key를 가진 요소를 찾아 해당 이터레이터를 반환한다.

만약 못찾으면 end()의 이터레이터를 반환합니다.

 

for(auto it : mp)

범위기반 for 루프 map을 순회하면 key는 first로 참조가 가능하고 value는 second로 참조합니다.

auto는 여기서 map<string, int>의 map이라면 map<string, int>::iterator::value_type을 반환하게 됩니다.

아마 길어서 auto를 자주 사용하는 것 같습니다.

 

for(auto it  = mp.begin(); it != mp.end(); ++it)

map에 있는 요소들을 이터레이터로 순회하는 방법, 범위기반 for루프가 나오기 전 많이 쓰던 방식이었습니다.

 

mp.clear()

map에 있는 요소들을 다 제거합니다.

 

 

 

 

주의할 점

맵을 사용할 때 주의할 점은 해당 인덱스에 참조만 해도 맵의 요소가 생깁니다.
만약 map에 해당 키에 해당하는 요소가 없으면 0 또는 빈 문자열로 초기화가 되어 (key, value)가 할당이 됩니다.
할당하고 싶지 않아도 대괄호 [ ]로 참조하게 되면 자동으로 요소가 추가되기 때문에 조심해야 합니다.

 

 

#include <iostream>
#include <map>

using namespace std;

int main()
{
    map<int, int> mp;
    map<string, string> mp2;
    
    // 0으로 초기화 하고 출력
    cout << mp[1] << '\n';
    
    // 빈 문자열로 초기화하고 출력
    cout << mp2["Santos"] << '\n';
    
    // 1 0
    for (auto it : mp)
        cout << it.first << " " << it.second;
        
    cout << '\n';
    
    // Santos 
    for (auto it : mp2)
        cout << it.first << " " << it.second;
        
    return 0;
}

이걸 한 번 돌려보면 알 것이다.

 

그래서 맵의 요소가 있는지 없는지를 확인해야 하는 것이 필요합니다.

#include <iostream>
#include <map>

using namespace std;

int main()
{
    map<int, int> mp;
    map<string, string> mp2;
    
    if (mp[1] == 0)
    {
        mp[1] = 2;
    }
    
    for (auto it : mp)
        cout << it.first << " " << it.second;
        
    return 0;
}

이런식으로 체크하는 것이 좋습니다.

다만 이 또한 문제가 있습니다.

해당 키에 0이 아닌 값이 들어가야 활용이 되는 부분입니다.

하지만 이미 if문 에서 mp[1] == 0을 해버린 순간 이미 초기화가 되어버립니다.

 

그래서 이러한 방식보다는 아래 방식이 더 좋습니다.

 

#include <iostream>
#include <map>

using namespace std;

int main()
{
    map<int, int> mp;
    map<string, string> mp2;
    
    // find를 통해 찾아보고 그 값이
    // end()라면 못찾았다는 뜻
    if (mp.find(1) == mp.end())
    {
        mp[1] = 2;
    }
    
    for (auto it : mp) cout << it.first << " " << it.second;
    
    return 0;
}

 

 

후아 이거 작성하다가 중간에 너무 졸려서 낮잠을 자버렸당.....

이제 낮잠을 2시간을 자버리다니 큰일이에요.. ㅠ

 

아무튼 끝!

'내 개인적인 공부 > 자료구조' 카테고리의 다른 글

set  (0) 2024.04.13
unordered_map  (0) 2024.04.13
배열 vs 연결리스트  (0) 2024.04.13
랜덤접근과 순차적 접근?  (0) 2024.04.13
Linked_List  (0) 2024.04.13