Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

경쟁 프로그래밍과 코딩 테스트에 유용한 C++ 트릭 총정리 (C++11)

경쟁 프로그래밍이나 코딩 테스트에서는 문제 해결 능력만큼이나 코드를 얼마나 빠르게 작성하느냐가 중요합니다. 잘 알려진 C++의 숨은 기능들을 활용하면 불필요한 구현 시간을 줄이고 실수 가능성도 낮출 수 있습니다. 이 글에서는 C++11 기준으로 실전에서 바로 쓸 수 있는 유용한 트릭들을 하나씩 소개합니다.

1. % 연산자 없이 홀수·짝수 판별하기

나머지 연산자(%)를 쓰지 않고도 비트 AND 연산으로 홀수와 짝수를 구분할 수 있습니다. 모든 홀수는 최하위 비트(LSb)가 1이므로, 숫자와 1을 AND 연산하면 최하위 비트만 남게 됩니다. 결과가 0이 아니면 홀수, 0이면 짝수입니다.

if ((n & 1) != 0) {
    // 홀수
} else {
    // 짝수
}

2. 시프트 연산자로 곱셈·나눗셈 빠르게 처리하기

어떤 수에 2n을 곱하려면 왼쪽으로 n번 시프트하고, 2n으로 나누려면 오른쪽으로 n번 시프트하면 됩니다. 곱셈·나눗셈 연산보다 비트 시프트가 더 빠르게 처리됩니다.

int x = 40;
int y = x << 2; // 40 * 4 = 160

cout << y << endl; // 160

y = x >> 2;       // 40 / 4 = 10
cout << y << endl; // 10

3. 임시 변수 없이 두 수 교환하기

덧셈과 뺄셈으로도 두 변수를 교환할 수 있지만, 비트 XOR 연산을 사용하면 더 간결하게 처리할 수 있습니다.

// x와 y 교환
x ^= y;
y ^= x;
x ^= y;

4. strlen() 없이 문자열 순회하기

strlen() 함수를 사용할 수 없는 제약 조건이 있더라도 걱정할 필요가 없습니다. C 스타일 문자열은 널 문자('\0')로 끝나므로, 해당 위치의 문자가 유효한 값(0이 아닌 값)인지만 검사하면 됩니다.

for (int i = 0; s[i]; i++) {
    cout << s[i];
}

5. push_back() 대신 emplace_back() 사용하기

STL 컨테이너(vector 등)에 새 요소를 추가할 때 흔히 push_back()을 사용하지만, emplace_back()을 사용하면 더 빠릅니다. 이 함수는 별도의 임시 메모리를 할당하지 않고 컨테이너 내부에서 직접 객체를 생성해 성능상 이점이 있습니다.

6. 내장 GCD 함수 활용하기

C++은 최대공약수(GCD)를 구하는 내장 함수를 제공합니다. 직접 유클리드 호제법을 구현할 필요 없이 다음과 같이 바로 사용할 수 있습니다.

__gcd(x, y) // x와 y의 최대공약수 반환

7. 배열 크기 제한 이해하기

main 함수 안에서 지역 배열로 선언하면 스택 크기 제한 때문에 약 106 정도까지만 선언할 수 있습니다. 하지만 배열을 전역(global)으로 선언하면 최대 107 크기까지 확보할 수 있습니다. 큰 배열이 필요한 문제라면 전역 선언을 활용하세요.

8. log10으로 최상위 자릿수 구하기

로그 연산을 이용하면 어떤 수의 가장 앞자리 숫자(최상위 자릿수)를 쉽게 구할 수 있습니다.

int n = 4578;
double k = log10(n);
k = k - floor(k);
int x = pow(10, k); // x = 4 (최상위 자릿수)

9. 반복문 없이 자릿수 개수 구하기

반복문으로 한 자리씩 세는 대신 로그를 사용하면 한 줄로 자릿수를 계산할 수 있습니다.

int n = 4578;
int digit_count = floor(log10(n)) + 1; // 4

10. 2의 거듭제곱 여부 확인하기

2의 거듭제곱인 수는 이진 표현에서 비트가 하나만 1입니다. 따라서 x와 (x - 1)을 AND 연산하면 0이 되는 성질을 이용해 간단히 판별할 수 있습니다.

int x = 1024;
bool check = x && (!(x & (x - 1))); // true이면 2의 거듭제곱

11. all_of, any_of, none_of 조건 검사 알고리즘

C++은 배열 전체의 조건 만족 여부를 검사하는 내장 알고리즘을 제공합니다. 반복문을 직접 작성할 필요가 없습니다.

all_of(left, left + n, isPositive());  // 모든 요소가 양수인지 검사
any_of(left, left + n, isPositive());  // 양수인 요소가 하나라도 있는지 검사
none_of(left, left + n, isPositive()); // 양수인 요소가 하나도 없는지 검사

12. copy_n으로 컨테이너 복사하기

copy_n() 함수를 사용하면 지정한 개수만큼의 요소를 한 컨테이너에서 다른 컨테이너로 손쉽게 복사할 수 있습니다.

int src[5] = {10, 20, 30, 40, 50};
int dest[5];
copy_n(src, 5, dest);

13. iota로 연속된 값 생성하기

iota() 알고리즘은 시작 값을 첫 번째 요소에 저장한 뒤, 후위 증가 연산자처럼 값을 1씩 늘려가며 범위를 채웁니다. 숫자뿐 아니라 문자에도 사용할 수 있습니다.

int arr[5] = {0};
iota(arr, arr + 5, 15);   // {15, 16, 17, 18, 19} 생성

char str[5] = {0};
iota(str, str + 5, 'A');  // {'A', 'B', 'C', 'D', 'E'} 생성

14. 0b 접두사로 이진수 리터럴 사용하기

C++11부터는 숫자 앞에 0b 접두사를 붙여 이진수를 직접 표현할 수 있습니다. 비트 연산 문제에서 가독성이 크게 향상됩니다.

int x = 0b1101; // x에는 13이 저장됨

15. and 같은 대체 키워드 사용하기

C++에서는 조건 연산자 대신 읽기 쉬운 키워드 형태의 대체 표기를 사용할 수 있습니다. 예를 들어 '&&' 대신 'and'를 쓸 수 있습니다.

int x = 10;
if (x < 20 and x > 5)
    cout << "True" << endl;
else
    cout << "False" << endl;
// 출력: True

이 외에도 C++에는 경쟁 프로그래밍에 도움이 되는 다양한 내장 기능들이 있습니다. 위 트릭들을 미리 익혀두면 시험장에서 구현 시간을 크게 단축하고, 더 중요한 알고리즘 설계에 집중할 수 있습니다.