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;
}
ios_base::sync_with_stdio(false);
cin.tie(null);
//또는
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
ios_base::sync_with_stdio(false);의 장점
ios_base::sync_with_stdio 구문은 c의 stdio와 cpp의 iostream을 동기화시켜주는 역할을 하는데, 이 때 iostream과 stdio의 버퍼를 모두 사용하기 때문에 딜레이가 발생합니다.
이로 인해, 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으로 "이름을 입력하세요: "를 출력할 때 버퍼를 비워줘야 합니다.
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 : 식이 상수로 계산되지 않았습니다.
//함수
'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
...
#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;
}
#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;
}
#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;
}