알고리즘에 사용되는 C++ 문법

아현·2021년 7월 5일
0

C++

목록 보기
4/5

1. C++ pair 사용하여 쌍으로 값

참고


Pair<[Type], [Type] > 이란?

  • 2개의 각 각 지정한 타입의 값을 저장한다.

  • 저장한 값은 .first.second로 각각 접근할 수 있다.

  • 2개의 연관된 값을 같이 저장할 수 있어서 관리를 용이하게 할 수 있다.

  • 특히, 연관된 2개의 값에서 각각의 조건에 따라 정렬한 결과를 얻고자 할 때 사용하면 좋다.
    ( 즉, 2개의 정렬 조건으로 정렬을 하고 싶을 때)


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

bool comp(pair<int, double> a, pair<int, double> b) {
    if (a.second == b.second) a.first < b.first;
    return a.second > b.second;
}

int main() {
    vector<int> v;
    int N = 8;
    //pair변수를 선언하고 값을 할당한다. 값을 줄 때는 make_pair( 값1, 값2)를 사용
    pair< int, int> test = make_pair(1, 2); /
    vector<pair<int, double>> vv; // vector의 pair타입을 사용하여 선언함으로써 pair 변수를 저장할 수 있게 한다.
    v.push_back(1);
    v.push_back(3);
    v.push_back(2);
    v.push_back(1);
    v.push_back(0);
    
    for (int i = 0; i < v.size(); i++) {
    	//pair 타입을 저장하는 vector이므로 make_pair을 사용해 주어서 값을 저장
        vv.push_back(make_pair(i + 1, (double)v[i] / N)); 
        N -= v[i];
    } // (1, 0.125) (2, 0.4285) (3, 0.5) (4, 0.5) (5, 0) 순으로 데이터가 있다.
	

    sort(vv.begin(), vv.end(), comp); // 2번째 값이 큰 순으로, 1번째 값이 작은 순으로 정렬을 한다.
    
    for (int i = 0; i < vv.size(); i++) {
        cout << vv[i].first << " " << vv[i].second << "\n";
    }

	return 0;
}



2. ios_base::sync_with_stdio(false); cin.tie(null);

참조



ios_base::sync_with_stdio(false); 
cin.tie(null);

//또는

ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);

  • C++로 알고리즘을 풀 때 실행 속도를 높이기 이런 구문을 작성

  • ios_base::sync_with_stdio(false);의 장점

    • ios_base::sync_with_stdio 구문은 c의 stdio와 cpp의 iostream을 동기화시켜주는 역할을 하는데, 이 때 iostream과 stdio의 버퍼를 모두 사용하기 때문에 딜레이가 발생합니다.

      • 따라서, ios_base::sync_with_stdio(false); 코드를 작성해줌으로써 동기화를 비활성화시켜줍니다.
    • 이로 인해, c++만의 독립적인 버퍼가 생성되어 c의 버퍼와 병행하여 사용할 수 없게 되지만, 사용하는 버퍼의 수가 줄어들었기 때문에 실행 속도는 빨라지게 됩니다.

    • 알고리즘 문제를 풀 때는 대부분 싱글 쓰레드 환경이기 때문에 해당 코드를 추가해줘도 문제가 발생하지 않을 확률이 높습니다.

  • ios_base::sync_with_stdio(false);의 단점

    • 동기화된 C++ 버퍼의 경우 thread-safe하기 때문에 모든 IO의 순서가 예상한 것과 정확히 일치함을 보장할 수 있습니다.

      -> (output from different threads may interleave, but you get no data races).

    • 하지만, ios_base::sync_with_stdio(false); 코드를 추가함으로 인해 동기화가 비활성화됐기 때문에 멀티 쓰레드 환경에서는 출력 순서를 보장할 수 없습니다.

    • 그리고 버퍼가 분리되었기 때문에 cin과 C의 scanf, gets, getchar 등을 같이 사용하면 안되고 cout과 C의 printf, puts, putchar 등을 같이 사용하면 엉뚱한 결과가 나올 확률이 높기 때문에 사용하면 안 됩니다.


  • cin.tie(null);

    • 결론부터 말하자면 cin.tie(null); 코드는 cin과 cout의 묶음을 풀어줍니다.

    • 기본적으로 cin과 cout은 묶여있고 묶여있는 스트림들은 한 스트림이 다른 스트림에서 각 IO 작업을 진행하기 전에 자동으로 버퍼를 비워줌을 보장합니다

    • 기본적으로 cin으로 읽을 때 먼저 출력 버퍼를 비우는데, 마찬가지로 알고리즘을 풀 때는 화면에 바로 보이는 것이 중요하지 않습니다. 따라서, 입력과 출력을 여러 번 번갈아가며 반복해야 하는 경우 필수적으로 cin.tie(null); 코드를 추가하여 cout과 cin의 묶음을 풀어줘야 합니다.

   cout << "이름을 입력하세요: "; 
    cin >> name;

위 경우 cin과 cout이 묶여있기 때문에, 이름을 입력하기 전에 "이름을 입력하세요: " 구문이 먼저 출력될 것입니다.
하지만, cin.tie(null); 코드를 추가한다면 cin과 cout의 묶음이 풀리면서 "이름을 입력하세요: "; 구문이 먼저 출력되지도 않았는데 먼저 이름을 입력을 받는 경우가 발생할 수 있습니다.
이는 cout이 기본적으로 버퍼에 추가되고 바로 비워지지 않기 때문입니다. (출력 명령을 내리거나 버퍼가 가득 찼을 경우에만 버퍼를 비우고 출력합니다.)
따라서, cin.tie(null); 코드를 추가했고 name을 입력받기 전에 "이름을 입력하세요: " 구문을 먼저 보고 싶다면 cout으로 "이름을 입력하세요: "를 출력할 때 버퍼를 비워줘야 합니다.



3. constexpr

참고


  • C++11부터 지원되는 한정자 constexpr는 일반화된 상수 표현식(Generalized constant expression)을 사용할 수 있게 해주며, 일반화된 상수 표현식을 통해 변수나 함수, 생성자 함수에 대하여 컴파일 타임에 평가될 수 있도록 처리해 줄 수 있다.
    (C++17부터는 람다 함수에도 constexpr 키워드 사용이 가능하다)

  • constexpr 변수 또는 함수의 반환값은 반드시 LiteralType이어야 하며, LiteralType은 컴파일 타임에 해당 레이아웃이 결정될 수 있는 타입을 의미한다.

    • 다음은 리터럴 타입들의 유형이다.

      • void

      • 스칼라 값

      • 참조

      • void, 스칼라 값, 참조의 배열

      • trivial 소멸자 및 이동 또는 복사 생성자가 아닌 constexpr 생성자를 포함하는 클래스. 또한 해당 비정적 데이터 멤버 및 기본 클래스가 모두 리터럴 타입이고 volatile이 아니어야 함

  • 코드 작업 중 해당 타입이 리터럴 타입인지 확인하고 싶을 땐, std::is_literal_type을 사용하면 된다.

//변수

constexpr float x = 42.f;    // OK
constexpr float y { 108.f }; // OK
constexpr int i;             // error C2737: 'i': 'constexpr' 개체를 초기화해야 합니다.
int j = 0;
constexpr int k = j + 1;     // error C2131 : 식이 상수로 계산되지 않았습니다.


//함수




4. str[i] - '0'

참고

  • It converts a char to an integer by subtracting the ASCII value (48) of 0 from the char

'9' - '0' // 9

//Suppose the char numbers are '0-9' and suppose they are ACSII:
'0' - '0' = 48 - 48 = 0
'1' - '0' = 49 - 48 = 1
'2' - '0' = 50 - 48 = 2
...



5. 오름차순/ 내림차순 정렬

참고


오름차순


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

using namespace std;

int main() 
{
    vector<int> v = {3,2,6,5,8,1,9,7,4};
    sort(v.begin(), v.end());
    for(auto& i : v)
        cout << i << " ";
    
    return 0;
}

내림차순


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

using namespace std;

bool compare(int i, int j)
{
    return j < i;
}

int main() 
{
    vector<int> v = {3,2,6,5,8,1,9,7,4};
    sort(v.begin(), v.end(), compare);
    for(auto& i : v)
        cout << i << " ";
    
    return 0;
}


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

using namespace std;

int main() 
{
    vector<int> v = {3,2,6,5,8,1,9,7,4};
    sort(v.begin(), v.end(), greater<>());
    for(auto& i : v)
        cout << i << " ";
    
    return 0;
}



6. lower_bound, upper_bound

참고


lower_bound


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

int main() {

	int arr[6] = { 1,2,3,4,5,6 };
	cout << "lower_bound(6) : " << lower_bound(arr, arr + 6, 6) - arr;

    // 결과 -> lower_bound(6) : 5
    
	return 0;
}

  • 찾으려는 key 값보다 같거나 큰 숫자가 배열 몇 번째에서 처음 등장하는지 찾기 위함

    • 사용 조건 : 탐색을 진행할 배열 혹은 벡터는 오름차순 정렬되어 있어야 함
  • lower_bound의 반환형은 Iterator 이므로 실제로 몇 번째 인덱스인지 알고 싶다면, 위 코드와 같이 lower_bound 값에서 배열 첫 번째 주소를 가리키는 배열의 이름을 빼 주면 됩니다.

    • 벡터의 경우 아래와 같이 v.begin()을 빼 주면 됩니다.

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

int main() {

	vector<int> arr = { 1,2,3,4,5,6,6,6 };
	cout << "lower_bound(6) : " << lower_bound(arr.begin(), arr.end(), 6) - arr.begin();

    // 결과 -> lower_bound(6) : 5
    
	return 0;
}



upper_bound


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

int main() {

	vector<int> arr = { 1,2,3,4,5,6 };
	cout << "upper_bound(3) : " << upper_bound(arr.begin(), arr.end(), 3) - arr.begin();

    // 결과 -> upper_bound(3) : 3
	return 0;
}


  • 찾으려는 key 값을 초과하는 숫자가 배열 몇 번째에서 처음 등장하는지 찾기 위함

    • 사용 조건 : 탐색을 진행할 배열 혹은 벡터는 오름차순 정렬되어 있어야 함

사용 예시



int main() {

	vector<int> arr = { 1,3,5,5,5,8,8,10,10,11,13 };
	cout << "5의 갯수 : " << upper_bound(arr.begin(), arr.end(), 5) - lower_bound(arr.begin(), arr.end(), 5);

	return 0;
}

profile
For the sake of someone who studies computer science

0개의 댓글