Topic 2: STL 컨테이너 마스터하기 📦
🎯 학습 목표
- STL 컨테이너의 종류와 특징 이해하기
- vector, list, deque의 성능 차이 파악하기
- map, set 등 연관 컨테이너 활용하기
- 상황에 맞는 컨테이너 선택하기
- 실전 게임 리더보드 시스템 구현하기
🧰 STL 컨테이너가 뭐예요?
레고를 만들 때 다양한 형태의 블록들이 필요하듯이, 프로그래밍에서도 다양한 형태의 데이터 저장소가 필요해요! STL 컨테이너는 마치 만능 도구상자처럼 상황에 맞는 저장 방식을 제공합니다.
💡 비유: 요리할 때 접시, 그릇, 컵, 팬이 각각 다른 용도가 있듯이, 컨테이너도 각자 특별한 능력이 있어요!
📋 순차 컨테이너 (Sequential Containers)
1️⃣ vector - 동적 배열의 왕 👑
#include <iostream>
#include <vector>
using namespace std;
int main() {
// 플레이어 점수 관리
vector<int> scores;
// 점수 추가 (뒤에서부터)
scores.push_back(1200);
scores.push_back(850);
scores.push_back(2100);
scores.push_back(750);
cout << "🎮 현재 점수 목록: ";
for (int score : scores) {
cout << score << " ";
}
cout << "\n\n";
// 인덱스로 접근 (빠름! O(1))
cout << "🥇 최고 점수 확인: " << scores[2] << "점\n";
// 크기 정보
cout << "📊 총 플레이어 수: " << scores.size() << "명\n";
cout << "💾 현재 용량: " << scores.capacity() << "\n\n";
// 마지막 요소 제거
scores.pop_back();
cout << "❌ 마지막 플레이어 제거 후 크기: " << scores.size() << "\n";
return 0;
}🚀 vector의 장점:
- 인덱스 접근이 빠름 (O(1))
- 메모리 효율적
- 뒤에 요소 추가/제거가 빠름
⚠️ vector의 단점:
- 중간 삽입/삭제가 느림 (O(n))
- 용량 초과시 전체 복사 발생
2️⃣ list - 연결 리스트의 달인 🔗
#include <iostream>
#include <list>
#include <string>
using namespace std;
int main() {
// 대기열 관리 (앞뒤 삽입이 자유로움)
list<string> waitingQueue;
// 일반 플레이어들 추가
waitingQueue.push_back("Player1");
waitingQueue.push_back("Player2");
waitingQueue.push_back("Player3");
// VIP 플레이어는 맨 앞으로!
waitingQueue.push_front("VIP_Player");
cout << "🎯 현재 대기열:\n";
for (const auto& player : waitingQueue) {
cout << " 👤 " << player << "\n";
}
// 중간에 특별 플레이어 삽입
auto it = waitingQueue.begin();
advance(it, 2); // 2번째 위치로 이동
waitingQueue.insert(it, "Special_Player");
cout << "\n🌟 특별 플레이어 추가 후:\n";
for (const auto& player : waitingQueue) {
cout << " 👤 " << player << "\n";
}
return 0;
}🚀 list의 장점:
- 중간 삽입/삭제가 빠름 (O(1))
- 앞뒤 모든 위치에서 효율적
⚠️ list의 단점:
- 인덱스 접근 불가 (순차 접근만)
- 메모리 오버헤드 있음
3️⃣ deque - 양쪽 끝의 마법사 🎭
#include <iostream>
#include <deque>
using namespace std;
int main() {
// 최근 게임 히스토리 (앞뒤로 추가/제거)
deque<string> gameHistory;
// 게임 기록 추가
gameHistory.push_back("Level 1 Complete");
gameHistory.push_back("Level 2 Complete");
gameHistory.push_back("Level 3 Failed");
// 긴급 공지는 맨 앞으로
gameHistory.push_front("🚨 Emergency Update!");
cout << "📜 게임 히스토리:\n";
for (int i = 0; i < gameHistory.size(); i++) {
cout << "[" << i << "] " << gameHistory[i] << "\n";
}
// 오래된 기록 제거
gameHistory.pop_back();
gameHistory.pop_front();
cout << "\n🧹 정리 후:\n";
for (const auto& record : gameHistory) {
cout << " 📝 " << record << "\n";
}
return 0;
}🗺️ 연관 컨테이너 (Associative Containers)
1️⃣ map - 키-값 쌍의 마스터 🗝️
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
// 플레이어별 최고 점수 관리
map<string, int> playerScores;
// 점수 등록
playerScores["Alice"] = 1500;
playerScores["Bob"] = 2100;
playerScores["Charlie"] = 800;
playerScores["Diana"] = 1800;
cout << "🏆 플레이어별 최고 점수:\n";
for (const auto& pair : playerScores) {
cout << "👤 " << pair.first << ": " << pair.second << "점\n";
}
// 특정 플레이어 점수 조회
string searchPlayer = "Bob";
if (playerScores.find(searchPlayer) != playerScores.end()) {
cout << "\n🔍 " << searchPlayer << "의 점수: "
<< playerScores[searchPlayer] << "점\n";
}
// 점수 업데이트
playerScores["Alice"] = 1650; // 새 최고 점수!
cout << "🆙 Alice 점수 업데이트: " << playerScores["Alice"] << "점\n";
return 0;
}2️⃣ set - 중복 없는 컬렉션 🎯
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main() {
// 유니크한 아이템 컬렉션
set<string> uniqueItems;
// 아이템 추가 (중복 자동 제거)
uniqueItems.insert("Magic Sword");
uniqueItems.insert("Shield of Light");
uniqueItems.insert("Magic Sword"); // 중복! 무시됨
uniqueItems.insert("Health Potion");
uniqueItems.insert("Magic Sword"); // 또 중복! 무시됨
cout << "🎒 보유 아이템 (중복 제거됨):\n";
for (const auto& item : uniqueItems) {
cout << " ✨ " << item << "\n";
}
// 아이템 보유 확인
string checkItem = "Magic Sword";
if (uniqueItems.count(checkItem)) {
cout << "\n✅ " << checkItem << " 보유중!\n";
}
cout << "📊 총 아이템 수: " << uniqueItems.size() << "개\n";
return 0;
}🎮 실전 프로젝트: 게임 리더보드 시스템
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <string>
using namespace std;
class GameLeaderboard {
private:
map<string, int> playerScores; // 플레이어별 최고 점수
set<string> activePlayers; // 현재 활성 플레이어
vector<pair<int, string>> rankings; // 랭킹 (점수, 이름)
public:
// 플레이어 점수 등록/업데이트
void updateScore(const string& player, int score) {
activePlayers.insert(player);
if (playerScores[player] < score) {
playerScores[player] = score;
cout << "🆙 " << player << " 새 최고 점수: " << score << "점!\n";
} else {
cout << "📊 " << player << " 점수: " << score << "점 (최고: "
<< playerScores[player] << "점)\n";
}
updateRankings();
}
// 랭킹 업데이트
void updateRankings() {
rankings.clear();
for (const auto& pair : playerScores) {
rankings.push_back({pair.second, pair.first});
}
// 점수 기준 내림차순 정렬
sort(rankings.begin(), rankings.end(), greater<pair<int, string>>());
}
// 상위 N명 출력
void showTopPlayers(int topN = 5) {
cout << "\n🏆 TOP " << topN << " 랭킹:\n";
cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
for (int i = 0; i < min(topN, (int)rankings.size()); i++) {
string medal = "";
if (i == 0) medal = "🥇";
else if (i == 1) medal = "🥈";
else if (i == 2) medal = "🥉";
else medal = "🏅";
cout << medal << " " << (i + 1) << "위: "
<< rankings[i].second << " - "
<< rankings[i].first << "점\n";
}
cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
}
// 플레이어 정보 조회
void showPlayerInfo(const string& player) {
if (activePlayers.count(player)) {
cout << "\n👤 " << player << " 정보:\n";
cout << " 💯 최고 점수: " << playerScores[player] << "점\n";
// 현재 순위 찾기
for (int i = 0; i < rankings.size(); i++) {
if (rankings[i].second == player) {
cout << " 🏆 현재 순위: " << (i + 1) << "위\n";
break;
}
}
} else {
cout << "❌ " << player << " 플레이어를 찾을 수 없습니다.\n";
}
}
// 전체 통계
void showStats() {
cout << "\n📈 게임 통계:\n";
cout << " 👥 총 플레이어 수: " << activePlayers.size() << "명\n";
if (!rankings.empty()) {
cout << " 🥇 최고 점수: " << rankings[0].first << "점\n";
cout << " 🥇 최고 점수 보유자: " << rankings[0].second << "\n";
}
}
};
int main() {
cout << "🎮 게임 리더보드 시스템 시작!\n\n";
GameLeaderboard leaderboard;
// 플레이어들 점수 등록
leaderboard.updateScore("Alice", 1200);
leaderboard.updateScore("Bob", 1500);
leaderboard.updateScore("Charlie", 800);
leaderboard.updateScore("Diana", 1800);
leaderboard.updateScore("Eve", 1350);
// Alice 점수 개선
leaderboard.updateScore("Alice", 1650);
// 신규 플레이어
leaderboard.updateScore("Frank", 2000);
// 랭킹 확인
leaderboard.showTopPlayers();
// 특정 플레이어 정보
leaderboard.showPlayerInfo("Alice");
leaderboard.showPlayerInfo("Bob");
// 전체 통계
leaderboard.showStats();
return 0;
}🎯 컨테이너 선택 가이드
| 상황 | 추천 컨테이너 | 이유 |
|---|---|---|
| 인덱스로 빠른 접근 필요 | vector | O(1) 인덱스 접근 |
| 중간에 자주 삽입/삭제 | list | O(1) 삽입/삭제 |
| 양쪽 끝에서 삽입/삭제 | deque | 앞뒤 모두 O(1) |
| 키로 값 찾기 | map | O(log n) 검색 |
| 중복 없는 모음 | set | 자동 중복 제거 |
| 정렬 필요없는 빠른 검색 | unordered_map/set | O(1) 평균 검색 |
💡 핵심 정리
- vector: 동적 배열, 인덱스 접근 강점 📋
- list: 연결 리스트, 중간 삽입/삭제 강점 🔗
- deque: 양방향 큐, 앞뒤 삽입/삭제 강점 🎭
- map: 키-값 쌍, 검색과 정렬 강점 🗝️
- set: 중복 없는 집합, 유니크 보장 🎯
- 상황에 맞는 컨테이너 선택이 성능의 핵심! ⚡
✅ 실습 체크리스트
🚀 다음 시간 예고
다음 시간에는 STL 알고리즘과 반복자를 배워볼 예정이에요! 🔄
- 반복자로 컨테이너 순회하기
- sort, find, transform 등 유용한 알고리즘들
- 람다 표현식으로 더 간결한 코드 작성
- 게임 데이터 분석 시스템 구현
컨테이너에 데이터를 넣었다면, 이제 그 데이터를 마음대로 조작하는 방법을 배워보죠! 🎯
Last updated on