포스트

TIL 2026-02-26

TIL 2026-02-26

2/26 - 코딩테스트 강의 (이론 복습)

생성일: 2026년 2월 26일 오후 12:47

2-4 STL 사용하기

1. STL 구조 및 설계 원칙

  • STL 구성요소: 컨테이너 + 반복자 + 알고리즘

    → 알고리즘과 컨테이너의 분리 설계가 핵심 (유연한 재사용)

  • 템플릿 기반(Generic Programming)

    → 자료형 독립적 코드 작성 가능 (int, string, custom class 등)

  • 코테 관점에서 중요한 점

    자료구조 선택 = 시간복잡도 설계의 시작


2. vector (동적 배열) – 코테 핵심 1순위

연산시간복잡도유의점
push_back()O(1) (평균)capacity 초과 시 O(N)
insert() (중간)O(N)원소 밀림 발생
clear()O(N)전체 삭제
[] 접근O(1)인덱스 기반 접근 가능

핵심 메서드

  • push_back()
  • insert(pos, value)
  • reserve(n) → 재할당 방지
  • size(), capacity()
  • clear()

예시코드

1
2
3
4
vector<int>v;
v.reserve(100000);// 대량 입력 대비
v.push_back(1);
v[0] =10;

코테 유의점

  • 입력 개수 예측 가능 → reserve 필수
  • 중간 삽입 많으면 → deque 고려
  • 인덱스 접근 필요하면 → vector가 최적

3. set – 정렬 + 중복 제거

특징설명
내부 구조Red-Black Tree
삽입/삭제O(logN)
중복불가
정렬자동 오름차순

핵심 메서드

  • insert()
  • erase()
  • find()
  • clear()

예시코드

1
2
3
set<int>s;
s.insert(3);
s.erase(3);

코테 유의점

  • 자동 정렬 → 별도 sort 불필요
  • 중복 허용 필요 → multiset
  • 정렬 필요 없고 탐색만 중요 → unordered_set
  • 인덱스 접근 불가

4. map – Key-Value 자료구조

연산시간복잡도
삽입O(logN)
삭제O(logN)
탐색O(logN)
반복자 삭제평균 O(1)

핵심 메서드

  • m[key]
  • m.at(key)
  • insert()
  • erase()
  • find()

예시코드

1
2
3
4
map<string,int>m;
m["A"] =1;// 없으면 생성
m.at("A") =2;// 없으면 예외
m.erase("A");

코테 유의점

  • []는 자동 생성 → 실수로 값 추가될 수 있음
  • 존재 확인 → find() 사용 권장
  • 단순 키 집합이면 → set 사용
  • 키는 명확한 타입 (float 지양)

5. 반복자 (Iterator)

  • begin(), end()
  • rbegin(), rend()
  • 알고리즘은 반복자를 통해 작동

예시코드

1
2
for(autoit =v.begin();it!=v.end();++it)
cout<< *it;

코테 포인트

  • erase 후 반복자 무효화 주의
  • range-based for가 간결하고 안전

6. 알고리즘 (algorithm 헤더)

자주 쓰는 함수

함수설명
sort()정렬
find()탐색
count()개수
unique()중복 제거 (정렬 후 사용)

예시코드

1
2
sort(v.begin(),v.end());
autoit =find(v.begin(),v.end(),5);

코테 유의점

  • unique는 정렬 후 사용
  • 사용자 정의 정렬 → lambda 활용
1
2
3
sort(v.begin(),v.end(), [](inta,intb){
returna>b;
});

7. 코테 실전 선택 기준 정리

상황추천 컨테이너
인덱스 접근vector
정렬 + 중복 제거set
키-값 저장map
빠른 탐색만 필요unordered_map
삽입/삭제 많음deque

핵심 정리

  • 자료구조 선택 = 시간복잡도 설계
  • vector는 기본, set/map은 logN
  • reserve()는 대량 입력 문제 필수
  • [] vs at() 차이 숙지
  • 자동 정렬 특성 이해해야 실수 방지

2-5 알고리즘 효율성 (코딩테스트 관점 정리)

컨테이너 선택 기준 (시간복잡도 중심)

| 컨테이너 | 삽입 | 탐색 | 특징 | 사용 상황 | | — | — | — | — | — | | vector | O(1) (뒤) / O(N) (앞) | O(N) | 연속 메모리 | 임의접근 많을 때 | | deque | O(1) (앞/뒤) | O(1)* | 청크 구조 | 앞 삽입 많을 때 | | map | O(logN) | O(logN) | Red-Black Tree | 정렬 필요 시 | | unordered_map | O(1) 평균 | O(1) 평균 | 해시 | 정렬 불필요 + 빠른 탐색 |

  • 실제 체감 성능은 vector가 더 빠름

vector vs deque

  • vector.insert(begin(), x) → O(N) (전체 이동 발생)
  • deque.push_front(x) → O(1)
  • 임의접근은 둘 다 O(1)이지만 캐시 효율은 vector 우세
  • 코테에서 앞 삽입 많으면 deque, 그 외엔 vector

코드 요약:

1
2
vec.insert(vec.begin(),x);// O(N)
deq.push_front(x);// O(1)

유의점:

  • vector는 앞 삽입 절대 지양
  • 대량 push_back 시 reserve() 필수

map vs unordered_map

  • map: 내부 정렬 → O(logN)
  • unordered_map: 해시 → O(1) 평균
  • 정렬 필요 없으면 unordered_map 선택

코드 요약:

1
2
map<int,int>m;// O(logN)
unordered_map<int,int>um;// O(1) avg

유의점:

  • unordered_map은 해시 충돌 시 O(N)
  • 키 범위 작으면 vector로 치환 고려

탐색 성능 비교

  • vector + find → O(N)
  • map.find() → O(logN)
  • unordered_map.find() → O(1)

코드 요약:

1
2
3
find(vec.begin(),vec.end(),x);// O(N)
m.find(x);// O(logN)
um.find(x);// O(1)

유의점:

  • 빈번 탐색 문제는 vector 절대 금물
  • 데이터 크기 10^5 이상이면 해시 고려

문자열 결합 성능

방식시간복잡도특징
s = s + xO(N)매번 새 객체 생성
s += xO(1) amortized제자리 수정
append()O(1) amortized내부 버퍼 사용

코드 요약:

1
2
3
s+=x;// 추천
s.append(x);// 추천
s =s+x;// 지양

유의점:

  • 반복 결합 시 + 사용하면 시간초과 위험

auto vs auto& (복사 비용)

  • for(auto x : vec) → 복사 발생
  • for(auto& x : vec) → 참조 (복사 없음)
  • 큰 구조체일수록 차이 극심

코드 요약:

1
for(auto&x :vec) { }// 권장

유의점:

  • 수정 안 하면 const auto& 사용
  • range loop 기본은 참조로 습관화

정렬이 필요 없는 정렬 사용 금지

  • map은 삽입할 때마다 정렬 유지 → O(logN)
  • 단순 카운팅 문제는 unordered_map 사용
  • 키 범위 작으면 배열 사용 (최적)

코테에서의 핵심 전략

  • 앞 삽입 많다 → deque
  • 임의접근 많다 → vector
  • 빠른 탐색 필요 → unordered_map
  • 정렬 필요 → map
  • 문자열 반복 결합 → +=
  • 반복문은 auto& 기본값

강의 3-1 재귀함수


재귀 설계 핵심 구조 (Base + Recursive Step)

| 구성 | 목적 | 실수 시 문제 | | — | — | — | | 기저 조건 | 종료 보장 | 무한 재귀 | | 재귀 단계 | 문제 축소 | 호출 깊이 과다 |

  • 재귀는 문제 크기 감소 설계가 핵심
  • 반드시 기저 조건 먼저 설계
  • 시간복잡도는 호출 트리 구조로 판단

코드 요약:

1
2
if (n==0)return1;
return n*f(n-1);

재귀 호출과 스택 메모리 (Stack Frame)

  • 호출마다 스택 프레임 생성
  • 깊이 = 호출 깊이
  • 공간복잡도: O(depth)

유의점:

  • 깊이 N이면 공간 O(N)
  • 10^5 이상 깊이는 위험
  • 반복문 전환 고려

단순 재귀 vs 메모이제이션

방식시간복잡도특징
피보나치 단순 재귀O(2^N)중복 호출
메모이제이션O(N)결과 저장

문제점:

  • fibo(n-1) + fibo(n-2) → 중복 계산
  • 호출 수 지수 증가

코드 요약:

1
2
if (memo[n]!=-1)return memo[n];
memo[n] =f(n-1)+f(n-2);

유의점:

  • DP로 전환 가능
  • 배열 초기화 필수

DFS: 재귀 vs 반복 (Stack 사용)

| 구현 | 시간 | 공간 | | — | — | — | | 재귀 DFS | O(V+E) | O(depth) | | stack DFS | O(V+E) | O(V) |

  • 재귀는 암묵적 스택 사용
  • 반복은 명시적 stack 사용

코드 요약:

1
2
3
4
5
voidDFS(intnode){
visited[node] =true;
for(autonext :graph[node])
if(!visited[next])DFS(next);
}

유의점:

  • visited 처리 순서 중요
    • 그래프 깊이 크면 반복 권장

지수 연산 최적화 (분할 정복)

방식시간복잡도
단순 재귀 powO(N)
분할 정복 powO(logN)

핵심 아이디어:

  • N 짝수 → pow(X,N) = pow(X,N/2)^2
  • 문제 크기 절반 감소

코드 요약:

1
2
doublehalf =pow(x,n/2);
return (n%2==0)?half*half :half*half*x;

유의점:

  • 반드시 N==0 기저조건
  • long long 오버플로우 주의

재귀 사용이 효율적인 경우

  • 분할 정복 (병합정렬, 퀵정렬)
  • 트리/그래프 탐색
  • 구조가 자연스럽게 분기될 때

비효율적 경우:

  • 단순 반복 누적 문제
  • 깊이 큰 선형 구조

시간복잡도 판단 기준 (코테 기준)

  • 호출 1개씩 감소 → O(N)
  • 호출 2개씩 분기 → O(2^N)
  • 크기 절반 감소 → O(logN)

예시:

  • fibo 단순 → O(2^N)
  • pow 분할 → O(logN)
  • DFS → O(V+E)

실전 코테 전략

  • 깊이 최대치 계산 먼저
  • 중복 호출 여부 확인
  • 메모이제이션 가능 여부 판단
  • 스택 초과 가능성 점검
  • 가능하면 반복문 전환 고려

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.