4 minute read

고려사항

객체지향설계 수업을 듣게 되었고, 전에 C언어 수업을 듣지 않은 나는 어떠한 대책을 강구해야만 했다.

이에 python으로 풀던 매일의 알고리즘 문제를 c++로 적절히 변환하여 자연스럽게 접해보려고 한다.

원래의 python 코드

#S2_15666_N과 M (12)

def bt(n,m,a,s,seq,res):
  if len(seq)==m:
    res.append(seq)
    return

  prev=-1
  for i in range(s,n):
    if prev!=a[i]:
      bt(n,m,a,i,seq+[a[i]],res)
      prev=a[i]

  return res

n,m=map(int,input().split())
a=list(map(int,input().split()))
a.sort()
res=bt(n,m,a,0,[],[])

for i in res:
  print(" ".join(map(str,i)))

C++ 코드

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

void bt(int n, int m, vector<int>& a, int s, vector<int>& seq, vector<vector<int>>& res) {
    if (seq.size()==m) {
        res.push_back(seq);
        return;
    }
    
    int prev=-1;
    for (int i=s; i<n; i++) {
        if (prev != a[i]) {
            seq.push_back(a[i]);
            bt(n,m,a,i,seq,res);
            seq.pop_back();
            prev=a[i];
        }
    }
}

int main() {
    int n,m;
    cin >> n >> m;
    
    vector<int> a(n);
    for (int i=0; i<n; i++) {
        cin >> a[i];
    }
    
    sort(a.begin(), a.end());
    
    vector<vector<int>> res;
    vector<int> seq;
    
    bt(n,m,a,0,seq,res);
    
    for (const auto& r:res) {
        for (int i=0; i<r.size(); i++) {
            if (i>0) cout << " ";
            cout << r[i];
        }
        cout << "\n";
    }
    
    return 0;
}

헤더 파일들

#include <iostream>
#include <vector>
#include <algorithm>

*헤더 파일: C나 C++에서 자주 사용되는 함수, 상수, 데이터 타입, 클래스 등을 미리 선언해 놓은 파일로, 코드의 재사용성을 높이고 복잡한 프로그램의 모듈화를 도움. java나 python에서는 import로 패키지나 모듈을 불러옴. 비슷한 느낌인 듯.

  • <iostream>: c++에서 표준 입력을 이용하기 위함.
  • <vector>: 벡터를 사용하기 위함. java의 ArrayList와 유사하다고 함.
  • <algorithm>: 정렬같은 알고리즘 함수를 사용하기 위함. 이 코드에서는 sort()때문에 사용.



네임스페이스

using namespace std;

C++에서 std는 표준 라이브러리의 모든 구성 요소들이 포함된 네임스페이스.

자주 사용하는 cin, cout, vector, sort 등을 사용할 때마다 std::를 붙여야 하는데, 이 코드는 using namespace std;를 통해 이를 생략하고 사용할 수 있게 함.

  • 왜 std::를 붙여야 하는가?: 표준 라이브러리의 모든 요소들을 별도의 공간(std)에 배치하여 사용자가 정의한 다른 이름과 충돌하지 않도록 함. 표준 라이브러리의 함수를 사용할 때, 그 함수가 std 네임스페이스에 속해 있음을 명확히 할 수 있음.
  • 네임스페이스가 무엇인가?: C++에서 이름의 충돌을 방지하기 위한 개념으로, 서로 다른 코드에서 동일한 이름의 함수, 변수, 클래스 등을 구분하기 위해 사용.
  • java와 python과의 비교
    • Java: Java에는 네임스페이스라는 개념 대신 패키지(package)가 있음. 클래스나 메소드 이름 충돌을 피하기 위해 패키지를 사용하며, java.util.List와 같이 명시적으로 사용.
    • Python: Python에서는 모듈 자체가 네임스페이스 역할을 함. import로 모듈을 불러오고 math.sqrt처럼 모듈명 뒤에 점을 붙여서 접근할 수 있음. from math import sqrt처럼 부분적으로 가져올 수도 있음.





백트래킹 함수

void bt(int n, int m, vector<int>& a, int s, vector<int>& seq, vector<vector<int>>& res) {
    if (seq.size()==m) {
        res.push_back(seq);
        return;
    }
    
    int prev=-1;
    for (int i=s; i<n; i++) {
        if (prev != a[i]) {
            seq.push_back(a[i]);
            bt(n,m,a,i,seq,res);
            seq.pop_back();
            prev=a[i];
        }
    }
}

백트래킹이 쓰이긴 했는데 일단 C++ 자체에 집중하기로…

void bt(int n, int m, vector<int>& a, int s, vector<int>& seq, vector<vector<int>>& res) {
    // 함수 본문
}
  • C++에서는 함수의 반환형을 명시해야 함 (void, int 등).
  • C++에서는 변수를 참조로 전달할 때 참조자인 & 기호를 사용하여 참조(레퍼런스)를 나타냄. 이는 함수가 인자를 직접 수정할 수 있게 해줌.
  • Java에서도 함수의 반환형을 명시해야 하며, public, private 같은 접근 제어자가 필요함. Java에서는 참조(레퍼런스) 개념이 있지만, 기본 자료형(primitive type)은 값에 의한 호출, 객체는 참조에 의한 호출임.
  • Python에서는 함수 정의에 반환형을 명시하지 않으며, 함수는 기본적으로 객체나 값을 반환함. 모든 변수는 참조에 의한 호출이 기본이며, def 키워드를 사용해 함수를 정의함.



벡터

vector<int> a(n); // 크기가 n인 벡터 선언
vector<vector<int>> res; // 2차원 벡터 선언
  • 위 방식으로 선언(크기 명시 없이 선언 가능)
  • <> 이렇게 표현하는게 자바 제네릭 느낌



조건문

    if (seq.size()==m) {
        res.push_back(seq);
        return;
    }
    
    int prev=-1;
    for (int i=s; i<n; i++) {
        if (prev != a[i]) {
            seq.push_back(a[i]);
            bt(n,m,a,i,seq,res);
            seq.pop_back();
            prev=a[i];
        }
    }
  • 조건문 괄호로 묶고 중괄호 {} 사용으로 블록 명시
  • 배열 요소 접근 방식도 비슷한 듯
  • push_back: vector같은 동적 배열에 새로운 요소를 추가, 벡터의 크기 증가시킴.
  • pop_back: 벡터의 마지막 요소를 제거함. Python에서 list.pop()과 유사하고, Java에서는 remove(size() - 1)과 같은 동작을 수행.



main 함수

int main() {
    int n,m;
    cin >> n >> m;
    
    vector<int> a(n);
    for (int i=0; i<n; i++) {
        cin >> a[i];
    }
    
    sort(a.begin(), a.end());
    
    vector<vector<int>> res;
    vector<int> seq;
    
    bt(n,m,a,0,seq,res);
    
    for (const auto& r:res) {
        for (int i=0; i<r.size(); i++) {
            if (i>0) cout << " ";
            cout << r[i];
        }
        cout << "\n";
    }
    
    return 0;
}
  • cin » n » m;: 표준 입력으로 두 개의 값을 입력 받음
    • cin
      • C++ 표준 입력 스트림으로, 콘솔로부터 데이터를 입력받는 데 사용됨.
      • character input의 약자. 표준 입력(stdin)에서 데이터를 읽음.
      • Python에서는 input() 함수, Java에서는 Scanner 클래스의 nextInt()와 비슷한 역할
    • >> 연산자 (추출 연산자, Extraction Operator)
      • cin과 함께 사용하여 입력받은 값을 변수에 저장하는 역할
      • 입력된 값을 오른쪽에 있는 변수로 추출한다는 의미에서 »를 사용함.
      • 위 코드의 경우 사용자가 n,m을 공백이나 줄바꿈으로 구분하여 입력하면 n,m 순서로 각각에 할당됨.
  • sort(a.begin(), a.end());: 두 개의 iterator입력받아서 구간내의 요소를 오름차순으로 정렬함.
  • for (const auto& r : res)
    • for문이 참 특이하게 생겼다고 느꼈음.
    • Python의 for element in list처럼 컨테이너의 각 요소를 자동으로 순회함.
    • const auto&
      • const: 읽기 전용임을 의미. r을 수정할 필요가 없기 때문에 사용.
      • auto&: 자동으로 r의 타입을 추론하고, 참조로 받음. 여기서는 r이 벡터의 참조가 됨.
    • python으로 치면 ‘for r in res:’ 느낌
  • if (i > 0) cout « ” “;
    • 인덱스가 0보다 클 때만 공백을 출력하므로, 첫 번째 숫자 앞에는 공백이 출력되지 않도록 함.
    • python으로 치면 ‘if i > 0: print(“ “, end=””)’
    • 출력 연산자 (<<): 삽입 연산자(Insertion Operator)로, cout을 통해 출력할 데이터를 전달
    • 연산자를 연속으로 사용하여 여러 데이터를 한 번에 출력할 수 있음
      #include <iostream>
      using namespace std;
      
      int main() {
      int x = 10;
      cout << "Value of x is: " << x << "\n"; // Value of x is: 10
      return 0;
        }
      

return 0;

  • 반드시 사용해야 하는가? -> 그건 아님
  • C++ 표준에 따르면, main 함수에서 return 0;을 명시적으로 적지 않더라도, 컴파일러는 암묵적으로 return 0;을 추가해줌.
  • main 함수가 끝까지 실행되면 자동으로 0을 반환함.



ETC

Screenshot 2024-09-05 at 21 08 21 Buddy-Virty 음성인식 테스트 이후로 Xcode를 오랜만에 써봤다.

Leave a comment