1. 함수객체와 람다함수
알고리즘에 정의된 여러 함수들은 보통 아래의 두 형태를 가짐
template <typename Iter>
void do_something(Iter begin, Iter end); // 시작점과 끝점의 바로 뒤를 인자로 받음
template <typename Iter, typename Pred>
void do_something(Iter begin, Iter end, Pred pred) // 시작점과 끝점의 바로 뒤를 받고 pred로 특정 조건을 받음
즉 해당 알고리즘 함수를 사용할 범위뿐 아니라 조건도 추가할 수 있음
예를들어 정렬의 경우 기본적으로 오름차순이지만 내림차순으로 정렬하게 바꿀 수 있음
위의 pred 자리에 들어가는 특정한 조건을 서술자라고 부르며 pred자리에는 bool을 리턴하는 함수객체를 전달함
혹은 람다함수를 작성해서 함수객체를 대신할 수 있음
1) 함수 객체
struct is_odd {
bool operator()(const int& i) { return i % 2 == 1; }
}
// 또는
struct comp {
return a > b;
}
이렇게 작성하고 pred자리에 is_odd() 또는 comp 라고 작성하면 됨
2) 람다 함수
함수객체를 편하게 만들게 해줌, 임시 함수객체를 만든다는 느낌으로 이름이 없는 함수 객체를 만들 수 있음
/*
[capture list] (받는 인자) -> 리턴 타입 { 함수 본체 }
*/
[](int i) -> bool { return i % 2 == 1; }
/*
리턴타입은 컴파일러가 판단 가능하므로 생략할 수 있음
[capture list] (받는 인자) {함수 본체}
*/
[](int i) { return i % 2 == 1; }
/*
a와 b의 n과의 차이의 절대값이 작은게 앞으로 가도록,
만약 차이의 절대값이 같다면 더 큰게 앞으로 가도록
*/
sort(numlist.begin(), numlist.end(),
[](const auto& a, const auto& b) {
if(abs(a - n) == abs(b - n)) return a > b;
else return abs(a - n) < abs(b - n);
});
/*
위처럼 사용할때 return a > b처럼 앞이 크게 리턴하면 큰게 앞으로 가는 내림차순 정렬
return abs(a-n) < abs(b-n);처럼 뒤가 크게 하면 절대값이 더 큰게 뒤로가는 오름차순 정렬
*/
람다함수 비교 조건문 예시
/*
길이가 짧은 문자열이 앞에 오고 싶되 길이가 같다면 사전순 빠른 문자가 앞으로 가도록
*/
if (a.length() == b.length()) return a < b;
return a.length() < b.length();
// 그냥 a < b 하면 말 그대로 a와 b의 사전순 비교
// 길이가 더 짧은게 앞으로 가고 싶다면 a.length() < b.length()로 비교
2. 정렬 알고리즘
1) sort
일반적인 정렬함수
퀵소트 기반으로 구현되어 있지만 pivot을 정하는 조건을 최적화해서 항상 O(nlogn)으로 처리된다고 함
sort(vec.begin(), vec.end()); // 오름차순 정렬
sort(vec.begin(), vec.end(), greater<>()); // 내림차순 정렬
sort(vec.begin(), vec.end(), comp); // comp에 맞는 조건에 따라 정렬
2) partial_sort
배열의 일부분만 정렬
예를들어 100명의 학생 중 상위 10명의 성적순만 보고싶다면 sort로 다 정렬할 필요없이 10개만하면 됨
partial_sort(start, middle, end); // start ~ end 전체원소 중에서 start ~ middle까지만 정렬함
partial_sort(vec.begin(), vec.begin() + 3, vec.end());
/*
{ 5 3 1 6 4 7 2 }가 있을때 middle이 vec.begin() + 3이면
앞의 3자리만 전체 배열중 가장 작은것 3개가 오게 하고 그 뒤는 신경쓰지 않음
즉 { 1 2 3 6 5 7 4 } 형식으로 제일 앞 3개만 정렬되고 뒤는 그냥 랜덤하게 남김
*/
3) stable_sort
안정 정렬, 정렬을 하되 원소들간의 순서는 보존함
그냥 sort는 불안정 정렬임
안정 정렬이란 vector에 a = 1, b = 1로 있을때 sort는 a, b순서가 될수도 b, a순서가 될수도 있지만
stable_sort는 a, b 순서를 sort전과 똑같게 보존해줌
3. transform
원소 수정하기
벡터의 값들 전체를 다 1씩 더한다면 기본적으로는 for문으로 하나씩 접근해서 +1 해주겠지만 transform함수로 더 간단하게 할 수 있음
/*
transform (시작 반복자, 끝 반복자, 결과를 저장할 컨테이너의 시작 반복자, Pred);
*/
std::transform(vec.begin(), vec.end(), vec.begin(), [](int i){ return i + 1; });
4. all_of / any_of
all_of 함수는 인자로 받은 범위안의 모든 원소가 다 조건을 만족해야 true
any_of 함수는 하나라도 조건을 만족하면 true
return std::all_of(users.begin(), users.end(), [](User& user) { return user.level >= 15; });
/*
user level이 모두 다 15이상이면 true를 리턴함
*/
5. reverse
array나 vector나 string을 뒤집는 함수
reverse(vec.begin(), vec.end());
/*
{1, 2, 3, 4, 5}가 들어있던 vec을 reverse하면 {5, 4, 3, 2, 1}이 된다.
또한 문자열 뒤집기도 가능
*/
6. rotate
벡터의 값들을 옮겨서 회전시킴
rotate(v.begin(), v.begin() + 1, v.end()); // 한칸씩 왼쪽 이동
rotate(v.begin(), v.end() - 1, v.end()); // 한칸씩 오른쪽 이동
/*
매개변수는 (시작반복자, 누가 시작위치로 올지, 종료 반복자) 순으로
두번째 매개변수 위치에 있던 값이 첫 위치로 간다는 뜻
*/
7. swap
두 변수나 객체의 값 교환
swap(a, b);
또는
swap(객체, 객체);
8. 이분탐색
정렬된 벡터에서만 사용 가능
- lower_bound : x 이상인 값이 처음 나오는 iterator 반환
- upper_bound : x 초과인 값이 처음 나오는 iterator 반환
- equal_range : { lower_bound, upper_bound }로 구성된 pair 반환
- binary_search : x가 존재하는지 아닌지 true/false 반환
auto itr1 = lower_bound(v.begin(), v.end(), x); // x이상인 첫번째 원소의 위치 반환
auto itr2 = upper_bound(v.begin(), v.end(), x); // x초과인 첫번째 원소의 위치 반환
auto itr3 = equal_range(v.begin(), v.end(), x); // itr1과 itr2로 이루어진 pair 반환
bool isthere = binary_search(v.begin(), v.end(), x); // x가 있는지 없는지 true/false 반환
또한 upper_bound 결과 - lower_bound 결과로 값의 갯수를 알 수 있다.
count의 경우 O(N)이지만 이분탐색 함수를 사용하면 O(logN)이므로 정렬된 벡터라면 이분탐색으로 count하는게 유리하다.
9. remove / remove_if
vector의 경우 erase할때마다 iterator가 무효화돼서 불편한데 이를 remove를 이용해서 해결
먼저 erase의 형태는 두가지가 있음
erase(Iterator pos); // 해당 위치의 값을 지움
erase(Iterator first, Iterator last); // first부터 last 사이의 모든 원소를 지움
remove는 위에서 두번째 형식과 연계해서 쓰는데 아래처럼 사용 가능
vec.erase(remove(vec.begin(), vec.end(), 3), vec.end()); // vec에 있는 모든 3 제거
remove함수의 역할은 정확히는 제거하는게 아니라 값들을 뒤쪽으로 쉬프트만 해줌
3을 찾으면 3이 아닌 수들을 한칸씩 옮겨주면서 자리만 바꿔줌
실제 제거는 erase해줘야함
특정 조건에 맞을때 제거하고 싶다면 remove_if를 사용
remove_if의 세번째 인자로 조건을 넣어서 true면 제거, 아니면 제거하지 않음
struct is_odd {
bool operator()(const int& i) { return i % 2 == 1; }
};
vec.erase(remove_if(vec.begin(), vec.end(), is_odd()), vec.end());
10. inner_product
두 벡터의 값들을 같은 인덱스끼리 곱한 후 총합해서 return
v1[0] * v2[0] + v1[1] * v2[1] * ... + v1[10] * v2[10]
v2의 size는 v1의 size보다 크거나 같아야 함
int result = inner_product(v1.begin(), v1.end(), v2.begin(), 0);
11. 문자열 맨 앞 또는 맨 뒤의 연속된 값 지우기
이건 algorithm 헤더 기능은 아니고 string 클래스 기능인듯한데 같이 쓰기 좋아서 적어둠
- find_first_not_of("x") : 처음으로 "x"가 아닌 index 반환
- find_last_not_of("x") : 뒤에서부터 처음으로 "x"가 아닌 index 반환
- remove_prefix(size)는 앞 부분부터 size 만큼 삭제
- remove_suffix(size)는 뒷 부분부터 size 만큼 삭제
// 문자열에서 맨 앞의 연속된 0을 지우고 싶다면
s.remove_prefix(std::min(s.find_first_not_of("0"), s.size()));
// 맨 뒤는
s.remove_suffix(std::min(s.size() - s.find_last_not_of("0") - 1, s.size()));
bitset같은걸로 2진수 만들때 유용할 듯
string binary = bitset<8 * sizeof(N)>(N).to_string();
cout << binary.substr(binary.find_first_not_of('0'));
12. accumulate
이건 algorithm 헤더 기능은 아니고 numeric 헤더 기능인듯한데 같이 쓰는 경우가 많아서 적어둠
1) 벡터 내의 모든 원소의 합
#include <numeric>
/*
첫번째, 두번째 인자는 어디부터 어디까지, 세번째 인자는 sum의 초기값
만약 sum이 double이면 0.0으로 적어주면 됨
*/
int sum = accumulate(v.begin(), v.end(), 0);
2) 벡터 내의 모든 원소의 곱
int mul = accumulate(vec.begin(), vec.end(), 1, multiplies<int>());
3) string 벡터 이어붙이기
vector<string> v{"abc", "de", "f", "ghijk"};
string answer = accumulate(v.begin(), v.end(), string{});
// answer = "abcdefghijk"
13. 보이어-무어 알고리즘
문자열에서 특정 문자열을 찾을때 find를 쓰는 방법도 있지만 보이어-무어 알고리즘을 사용하면 훨씬 빠르다.
아래의 s에서 needle을 탐색해서 첫번째로 나온 iterator를 반환한다.
string s = "I believe I can fly I believe I can fly I believe I can fly";
string needle = "believe";
auto itr = search(s.begin(), s.end(), boyer_moore_searcher(needle.begin(), needle.end()));
if (itr != s.end()) cout << needle << " at : " << distance(s.begin(), it);
else cout << needle << " not found ";
※ 이후 내용들 아래 사이트 확인하고 또 정리해보기
https://modoocode.com/256