[Algorithm] 0x02 기초 코드 작성 요령 2

Donghun Ha·2022년 1월 15일
0

출처: https://blog.encrypted.gg/923?category=773649

  • 본 노션은 개인 공부를 위해 위 링크를 토대로 정리한 글입니다.

✏️ STL과 함수 인자

함수인자

  • 포인터가 아닌 일반 타입을 함수 인자로 보내면 복사돼서 넘어가기에 함수 외부의 값은 바뀌지 않는다.

  • 배열을 인자로 주면, 배열의 주소를 넘기는 것이니, 함수 내부에서 배열 요소 값을 바꿀 수 있다.

  • 참조자

    void swap1(int a, int b) {
    	int tmp = a;
    	a = b;
    	b = tmp;
    } // X
    
    void swap2(int *a, int *b) {
    	int tmp = *a;
    	*a = *b;
    	*b = tmp;
    } // O
    
    void swap3(int &a, int &b) {
    	int tmp = a;
    	a = b;
    	b = tmp;
    } // O

    C언어와 같이 변수를 복사해서는 값을 바꿀 수 없고 swap2처럼 포인터를 보내서 값을 바꿀 수 있다.

    C++에서는 해결법이 하나 더 있는데, 참조자를 사용하는 것이다.
    swap3에서 매개변수 앞에 &가 붙어있는 것을 볼 수 있는데, 이를 사용하면 a와 b가 참조자가 된다.

    저렇게 a와 b를 참조자로 만들면 함수 내의 코드에서는 그냥 대입하는 것 처럼 보이지만,
    원본의 주소를 받아와 원본을 바꾸는 행위가 된다.

STL(Standard Template Library) - vector

  • vector은 일종의 가변배열로 크기를 마음대로 늘렸다 줄였다 할 수 있다.

  • vectorvector 헤더에 선언되어있다.

  • STL을 함수 인자로 넘길 때

    void func1(vector<int> v) {
    	v[10] = 7; // vector을 쌩으로 함수 인자로 넣으면 복사해서 보낸다.
    }
    
    int main() {
    	vector<int> v(100); // int형이고 0으로 초기화된 100칸짜리 가변배열 v 선언
    	func1(v); // v가 복사되어서 들어가기 때문에 func1 외부 값은 변경되지 않는다.
    }
    bool cmp1(vector<int> v1, vector<int> v2, int idx) {
    	return v1[idx] > v2[idx];
    }
    // 위 함수는 v1과 v2를 모두 복사해야 하기 때문에, 의도한 바와 달리 O(N)의 시간복잡도를 가진다.
    
    bool cmp2(vector<int> &v1, vector<int> &v2, int idx) {
    	return v1[idx] > v2[idx];
    }
    // 참조자를 사용하면, 참조 대상의 주소 정보만 넘어오기 때문에,
    // 시간복잡도는 의도한 대로 O(1)이 된다.

⌨️ 표준 입출력

scanf/printf와 cin/cout

C에서는 scanf / printf로 입출력을 처리하고, C++에서는 cin / cout을 사용하는데,
기능에 별 차이가 없으니 어느 것을 사용해도 상관이 없다.

  • 주의점 - 공백을 포함한 문자열을 입력받기가 껄끄럽다.(둘 다 공백 앞까지 입력을 받는다.) 해결책 - getline을 이용한다.(다른 방법이 있지만, 이게 제일 깔끔)

cin/cout 사용 시 주의점

  • 입출력으로 인한 시간초과를 막기 위해 ios::sync_with_stdio(0), cin.tie(0)이라는 두 명령을 실행시켜야 한다.
    (이를 하지 않으면 입/출력 양이 많을 때, 시간초과가 날 수 있다.)
    (아래 내용을 이해하지 않고 사용해도 상관은 없다.)

  • ios::sync_with_stdio(0)

    scanf / printf에서 쓰는 C 스트림과 cin / cout이 쓰는 C++ 스트림은 분리되어 있는데,
    기본적으로 프로그램은 두 스트림 모두 동기화 하고 있다.

    하지만, 내가 C++ 스트림만 사용한다면 두 스트림 모두 동기화할 필요가 없기 때문에
    위 명령어로 동기화를 끊는다.
    (동기화를 끊으면 printf / scanf를 사용하면 안된다.)

    ios::sync_with_stdio(false) == ios::sync_with_stdio(0)

  • cin.tie(0)

    입출력 시 버퍼를 사용하는데, 출력을 예시로 할 때,
    출력이 바로 콘솔로 나오는 것이 아닌, 버퍼에 넣었다가 나오게 되는데,

    문제 해결 시, 입력과 출력이 번갈아 나오고 그게 한 화면에서 보여지는 경우 버퍼의 존재로 인해 순서가 꼬일 수 있다.

    이러한 현상을 막기 위해 기본적으로 cin 명령 수행 전 cout 버퍼를 비우는데,
    코딩테스트에서는 입력과 출력 글자 사이에 순서가 꼬인다고 해도 채점에 영향을 주지 않는다.

    그렇기에 굳이 cin을 수행하기 전에 cout을 비우지 않아도 되기 때문에 더 빠르게 실행할 수 있다.

    cin.tie(nullptr) == cin.tie(0)

endl

endl 쓰지마세요.

절대절대절대절대 쓰지말자.

endl은 개행문자 출력 후 출력 버퍼를 비우라는 명령이지만, 코테는 버퍼를 비울 필요가 없다.

그러니 endl 대신 개행문자(\n)를 사용하자.

💁 코드 작성 팁

코딩테스트와 개발은 다르다.

실제 개발을 오래했던 사람은 코드를 아주 정교하게 짜는 경우가 더러 있다.

하지만, 코딩테스트는 내가 헷갈리지 않는 범위 안에서 어떻게든 타이핑을 아끼는 게 최고다.

💡 코딩테스트의 목표는 남이 알아볼 수 있는 클린코드를 작성하는 게 아니다.
어떻게든 제한된 시간 안에 정답을 받아야 한다.
그렇기 때문에, 깔끔하게 만들기 위해 노력하기 보다는 좀 더럽더라도
내가 빠르게 짤 수 있는 방식으로 빠르게 구현하는 것이 중요하다.

출력 마지막에 공백 또는 줄바꿈이 추가로 있어도 상관없다.

있어도, 없어도 정답 처리가 되기 때문에 별도로 예외처리 할 필요가 없다.

디버거는 굳이 사용하지 않아도 된다.

답이 올바르게 나오지 않을 때, 디버거를 사용하는 경우가 있는데,
코딩테스트의 코드는 길어야 100줄 전후의 길이일 것이다.

문제가 있을 때, 디버거를 켜서 더 늪에 빠지는 느낌을 받을 수 있기 때문에
차라리 중간 변수를 보고 싶으면 중간에서 출력을 확인하고 디버거는 사용하지 않는 것을 권장한다.

profile
Corca Backend Engineer, dha

1개의 댓글

comment-user-thumbnail
2022년 1월 16일

저도 코딩테스트를 C++로 준비하려고 하는데, 좋은 팁들 얻어갑니다! 😄

답글 달기