C++ 입문노트

5 min#ps#leetcode#cpp

나는 Java, Kotlin이 주력 언어다. 업무에서도 이 두 가지를 쓰지만 C++를 알아두면 선택지가 넓어질 것 같아 입문해보려고 한다. LeetCode를 풀면서 C++ 숙련도를 올려보자.

오늘 배운 것

1. std::vector (동적 배열) 선언법

C++에서는 객체를 생성할 때 new를 무분별하게 사용하지 않는다.

잘못된 예: vector<int> res = new vector(); (자바 스타일)

C++에서 new를 쓰면 메모리 주소(포인터)를 반환하기 때문에 vector<int>* res처럼 선언해야 하고, 나중에 직접 delete로 메모리를 해제해야 한다.

올바른 예: vector<int> res(크기);

이렇게 선언하면 함수가 끝날 때 자동으로 메모리가 정리되는 지역 변수가 된다.

2. .size() 메서드

배열이나 리스트의 길이를 구할 때 언어마다 이름이 다르지만, C++에서는 size()를 표준으로 사용한다.

3. 참조자 (Reference, &)

코드의 vector<int>& nums에서 & 기호는 원본을 가져다 쓰겠다는 뜻이다.

  • vector<int> nums: 함수를 호출할 때 배열 전체를 복사 (메모리 낭비가 큼)
  • vector<int>& nums: 원본 배열의 별명만 지어줌 (메모리 사용이 거의 없고 빠름)

4. 벡터의 초기화와 인덱스 접근

C++ 벡터는 선언할 때 크기를 미리 정해두면 일반 배열처럼 []를 사용해 접근할 수 있다.

vector<int> v(10);: 크기가 10이고 0으로 초기화된 벡터 생성. v[0]부터 v[9]까지 사용 가능.

크기를 정하지 않고 생성(vector<int> v;)했다면, v[i] = 10;처럼 대입할 수 없고 반드시 v.push_back(10);을 써야 한다.

5. 스택(Stack) vs 힙(Heap) - new의 의미가 다르다

Java에서 new는 일상적이지만, C++에서 new이 객체를 수동으로 관리하겠다는 선언에 가깝다.

Java: Solution sol = new Solution(); // GC(Garbage Collector)가 알아서 치워준다.

  • C++ (Stack): Solution sol; // 함수가 종료되면 메모리에서 자동으로 사라진다.
  • C++ (Heap): Solution* sol = new Solution(); // 사용 후 delete sol;을 하지 않으면 메모리 누수가 발생한다.

6. 네임스페이스 (std::) - 가문 밝히기

C++의 표준 라이브러리(STL)는 모두 std 네임스페이스 안에 있다.

Java는 import 후 클래스 이름만 쓰면 되지만, C++은 std::vector, std::sort처럼 출처를 밝히는 것이 원칙이다.

코드 상단에 using namespace std;를 선언하면 생략할 수 있으나, 규모가 큰 프로젝트에서는 이름 충돌 방지를 위해 std::를 명시하는 습관이 좋다.

7. 범위 기반 for 루프 (Range-based for loop)

Kotlin의 for (num in nums)나 Java의 for (int num : nums)와 같은 문법을 C++11부터 지원한다.

for (int n : nums) { 
    // n은 nums 요소를 '복사'해서 가져온다.
}

for (int& n : nums) { 
    // &를 붙이면 '참조'로 가져온다.
    // n을 수정하면 원본 nums도 수정되고, 복사 비용이 들지 않아 효율적이다.
}

Java/Kotlin -> C++ 대응표

Java / KotlinC++ STL핵심 특징
ArrayList<T>vector<T>연속 메모리, 인덱스 접근 O(1)
LinkedList<T>list<T>중간 삽입/삭제 O(1), 인덱스 접근 느림
Stack<T>stack<T>LIFO
Queue<T>queue<T>FIFO
Deque<T>deque<T>양쪽 삽입/삭제 O(1)
PriorityQueue<T>priority_queue<T>기본 최대 힙
HashMap<K,V>unordered_map<K,V>평균 O(1), 정렬 없음
TreeMap<K,V>map<K,V>자동 정렬, O(logN)
HashSet<T>unordered_set<T>평균 O(1), 중복 없음
TreeSet<T>set<T>정렬 유지, 중복 없음

코테에서 자주 쓰는 선택 기준

  • "순서대로 담고 인덱스로 빠르게 접근" -> vector
  • "키 기준 정렬된 순회 필요" -> map / set
  • "정렬 필요 없고 존재 여부만 빠르게" -> unordered_map / unordered_set
  • "최솟값/최댓값을 계속 꺼내야 함" -> priority_queue
  • "BFS 레벨 탐색" -> queue
  • "슬라이딩 윈도우에서 앞뒤 pop/push" -> deque

꼭 알아둘 기본 시간복잡도 (암기용)

  • vector: 뒤 push_back 평균 O(1), 중간 삽입/삭제 O(N), 인덱스 접근 O(1)
  • map/set: 삽입/삭제/탐색 O(logN)
  • unordered_map/unordered_set: 평균 O(1), 최악 O(N)
  • priority_queue: 삽입 O(logN), top 조회 O(1), pop O(logN)

실전 코드 패턴

#include <bits/stdc++.h>
using namespace std;

// 1) 최소 힙 (Java PriorityQueue 기본 동작과 유사)
priority_queue<int, vector<int>, greater<int>> minHeap;

// 2) 빈도수 카운트
unordered_map<int, int> freq;
for (int x : nums) freq[x]++;

// 3) key 정렬 순회가 필요할 때
map<int, int> ordered;
for (int x : nums) ordered[x]++;

// 4) 중복 제거 + 존재 여부 확인
unordered_set<int> seen;
if (seen.count(x)) {
    // already exists
}
seen.insert(x);

bits/stdc++.h 없이 필요한 헤더만 include 하기

온라인 저지에서는 #include <bits/stdc++.h>가 편하지만, 표준 C++ 헤더는 아니기 때문에 환경에 따라 동작하지 않을 수 있다. 실무/이식성을 생각하면 필요한 헤더를 명시적으로 include하는 습관이 좋다.

#include <iostream>      // cin, cout
#include <vector>        // vector
#include <string>        // string
#include <algorithm>     // sort, max, min, lower_bound
#include <unordered_map> // unordered_map
#include <unordered_set> // unordered_set
#include <map>           // map
#include <set>           // set
#include <queue>         // queue, priority_queue
#include <stack>         // stack
#include <deque>         // deque
#include <utility>       // pair
#include <limits>        // numeric_limits

using namespace std;

자주 쓰는 알고리즘 유틸까지 넣고 싶다면 다음도 많이 사용한다.

  • <functional>: greater<>, less<>, 함수 객체
  • <numeric>: accumulate, gcd(환경에 따라)
  • <tuple>: tuple, tie

C++ STL 사용 시 자주 하는 실수

  • unordered_map은 순서를 보장하지 않는다. 출력 순서 기대하면 안 됨
  • priority_queue는 기본이 최대 힙이다 (최소 힙은 comparator 필요)
  • vector에 크기 없이 v[i] = ... 하면 런타임 에러 가능성 있음
  • map[key]++는 key가 없으면 자동으로 0 생성 후 증가한다

정리하면, 코테에서는 대부분 vector + unordered_map/set + priority_queue 조합으로 시작하고, "정렬된 상태 유지"가 필요할 때만 map/set로 바꾸면 판단이 빨라진다.