본문 바로가기
알고리즘/자료 구조

C++로 알고리즘 풀이 시 필수 지식

by Jason95 2021. 1. 3.

헤더 파일

- Visual Studio에서는 math.h 파일을 include 하지 않아도 pow 함수를 사용할 수 있지만

  백준 환경에서는 math.h 파일을 include 하지 않고 pow 함수 사용 시 컴파일 오류가 난다.

 

변수 할당

- int : 4바이트 (-231 ~ 231-1) (약 -20억 ~ 약 20억)

- unsigned int : 4바이트 (0 ~ 232-1)

- long : 4바이트 (-231 ~ 231-1)

- long long : 8바이트 (-263 ~ 263-1)

- 더 큰 자료형 없음

 

변수 초기화

int arr[10];
int arr[10] = { 1, };

- 전역 변수를 첫째 줄처럼 선언하면 0이나 false와 같은 default value로 초기화되는 것이 보장된다는 의견과 보장되지 않는다는 의견이 분분하다.

- 둘째 줄처럼 초기화하면, 배열 내 모든 요소가 1로 초기화될 것 같지만, 실제로는 0번째 요소만 1로 초기화 된다.

- 프로그래머스 플랫폼에서는 {0, }로 초기화 하면 모두 0으로 초기화되는 듯 하다 (경험상 그런 것이니 확실치는 않음)

memset(arr, 0, sizeof(arr));
memset(arr, -1, sizeof(arr));

- 0이나 -1로 초기화할 때는 memset으로 초기화하는 것이 안전하고 빠르다. (0과 -1 이외의 숫자는 오류 발생)

- memset()은 <string.h>라는 헤더 파일 필요

vector <int> ans(N, -1);

- vector를 초기화할 때는, 위와 같이 하면, 크기가 N인 벡터의 모든 요소가 -1로 초기화된다.

vector<vector<int>> v(6, vector<int>(3, 10));

cout << v[0][0] << endl;
cout << v[1][2] << endl;
cout << v[5][1] << endl;
cout << v[2][5] << endl; // out of index

실행 결과

10
10
10

- 2차원 벡터의 초기화 방법이다.

 

입출력

ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);

위의 코드를 쓰지 않으면 cin와 cout이 성능을 떨어뜨린다.

뿐만 아니라 endl도 성능을 더욱 떨어뜨린다.

그래서 endl 대신 \n를 쓰면 성능이 좋아진다.

 

위의 방법들을 써도 시간 초과가 나는 경우에는

그냥 printf를 쓰는 게 제일 빠르다.

 

참고로, 백준 1874번과 2751번 같은 경우

scanf보다는 오히려 cin.tie(NULL)을 적용한 cin가 더 빨랐다.

 

문자/문자열

- str.substr(0, 3);

    * 0번째 index부터 3글자

- str.find('a');

- stoi('123');

    * string to int

    * #include <string> 필요

- to_string(123);

    * int to string

    * #include <string> 필요

- (int) '5'

    * char to int : 아스키 코드 값

- '5' - '0'

    * char to int : '5'를 5로 변환

- (char) 65;

    * int to char : 65를 'A'로 변환

- 8 + '0'

    * int to char: 8를 '8'로 변환

- getline(cin, str);

    * 한 문장 입력

- sort(vec.begin(), vec.end());

    * 사전 순 정렬

    * #include <algorithm> 필요

- 알파벳 개수 = 26개

- char 타입끼리 사칙연산

    * 숫자 타입으로 바뀜   ex) '5' - '0' = 5   /   'c' - 'a' = 2

- size() - 1

    * size()는 unsigned int를 리턴하므로c size()-1는 -1가 아니라 4294967295임을 주의 (특히 for문의 조건문에서 주의)

- string 타입을 const char* 타입으로 형변환

    * string cppStr = "CPPstring";

    * const char* cStr = cppStr.c_str()

    * printf("%s\n", cppStr.c_str());

- split by character

#include <iostream>
#include <vector>
#include <string>

using namespace std;

vector<string> split(string str, vector<char> characters) {
	vector<string> splited_strs;
	int idx = 0;
	int i = 0;
	for (; i < str.size(); i++) {
		bool flag = false;
		for (int j = 0; j < characters.size(); j++) {
			if (str[i] == characters[j]) flag = true;
		}
		if (flag == true) {
			splited_strs.push_back(str.substr(idx, i - idx));
			idx = i + 1;
		}
	}
	splited_strs.push_back(str.substr(idx, i - idx));
	return splited_strs;
}

int main() {
	string str; cin >> str; // 55-50+40
	vector<char> v;
	v.push_back('+');
	v.push_back('-');
	vector<string> strs = split(str, v);
	for (int i = 0; i < strs.size(); i++) {
		cout << strs[i] << endl;
	}
    
    /* 출력결과
	55
	50
	40
    */

	return 0;
}

 

정렬

- #include <algorithm> 선언하고 sort 함수 사용

- sort 함수는 quick sort 기반으로 평균 O(nlogn), 최악 O(n^2)임

    * 배열

      int arr[10];

      sort(arr, arr+10);

    * 벡터

      vector<int> vec;

      sort(vec.begin(), vec.end());

- 오름차순 정렬

sort(A.begin(), A.end());

또는

sort(A.begin(), A.end(), less<int>());

- 내림차순 정렬

sort(B.begin(), B.end(), greater<int>());

- 비교 함수의 customizing

    * bool 타입 리턴

    * a -> b 순서로 정렬하고 싶으면 return a < b 또는 a - b < 0

sort(dq.begin(), dq.end(), comp);
bool comp(pair<int, int> a, pair<int, int> b) {
	if (a.second == b.second) return a.first < b.first;
	else return a.second < b.second;
}

- 순서 뒤집기

reverse(arr, arr + size);

 

수열에서 최소 / 최대

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

using namespace std;

int arr[6] = { 3, 2, 1, 6, 4, 5 };
list<int> my_list = { 3, 2, 1, 6, 4, 5 };
vector<int> v = { 3, 2, 1, 6, 4, 5 };

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cout << *min_element(arr, arr + sizeof(arr) / sizeof(int)) << endl;
	cout << *max_element(my_list.begin(), my_list.end()) << endl;
	cout << *min_element(v.begin(), v.end()) << endl;

	return 0;
}

출력 결과 :

1
6
1

 

대소 비교

#include <iostream>
#include <algorithm>

using namespace std;

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cout << "min(1, 2)       : " << min(1, 2) << endl;
	cout << "min(2, 2)       : " << min(2, 2) << endl;
	cout << "min('b', 'd')   : " << min('b', 'd') << endl;
	cout << "min(34.5, 12.3) : " << min(34.5, 12.3) << endl << endl;

	cout << "max(1, 2)       : " << max(1, 2) << endl;
	cout << "max(2, 2)       : " << max(2, 2) << endl;
	cout << "max('b', 'd')   : " << max('b', 'd') << endl;
	cout << "max(34.5, 12.3) : " << max(34.5, 12.3) << endl;

	return 0;
}

출력 결과 :

min(1, 2)       : 1
min(2, 2)       : 2
min('b', 'd')   : b
min(34.5, 12.3) : 12.3

max(1, 2)       : 2
max(2, 2)       : 2
max('b', 'd')   : d
max(34.5, 12.3) : 34.5

- INT_MAX와 INT_MIN은 <limits.h>라는 헤더 파일에 있음

 

계산

- pow(2, 4);

    * 2의 4승 = 16

    * #include <cmath> 필요

- sqrt(4);

    * 루트 4 = 2

    * #include <cmath> 필요

- abs(4); abs(4.21);

    * #include <cstdlib>

        1) int abs(int num);

        2) long int abs(long int num);

        3) long long int abs(long long int num);

    * #include <cmath>

        1) double abs(double num)

        2) float abs(float num)

        3) long double abs(long double num)

        4) double abs(T x);

 

스택 오버플로우

1) 재귀 함수 과다 호출

2) 지역 변수 과다 할당

-> 기본 스택 사이즈인 1MB 초과 할당 시, 스택 오버플로우로 프로그램 멈춤

-> Visual Studio 기준, '속성 - 링커 - 시스템 - 스택 예약 크기'를 통해 스택 사이즈 수동 조절 가능 (단위 : Byte)

 

 

STL 활용

삼성 역량 테스트 B형을 제외한 대부분의 코딩 테스트에서는 모든 STL이 사용 가능하므로

탐색, 정렬, 트리 등을 직접 구현할 필요 없이 STL의 사용법만 익혀 놓으면 된다.

단, 기술면접에서는 각 자료 구조와 알고리즘에 대한 세부 사항을 물어볼 수 있으니

내부 구조와 구현 방법, 동작 원리, 시간 복잡도 비교 등은 알고 있어야 한다.

 

익혀야 할 주요 STL : vector, map, set, lower_bound, upper_bound, next_permutation, sort, iterator 등

 

자료구조

1) priority_queue

 - #include <queue> 선언하여 사용

 - 주요 멤버 함수 : size(), empty(), push(), pop(), top()

priority_queue<int> pq; // 자료형만 썼을 때 아래와 같은 default 값이 적용된다.
priority_queue<int, vector<int>, less<int>> pq; // less than operator 사용

 - default가 최대힙(우선순위가 높을수록 큰 값)이다. 즉 less<int> (오름차순) 사용

 - 최소힙(우선순위가 높을수록 작은 값)을 사용하려면 greater<int> (내림차순) 사용

priority_queue<pair<int, int>> pq;
priority_queue<pair<int, int>, vector<pair<int, int>>, less<pair<int,int>> pq;

 - pair타입은 first를 기준으로 정렬됨

 - custom comparator 사용법

typedef pair<int, int> my_type;

struct comp {
	bool operator()(my_type& a, my_type& b) {
		if (a.first == b.first) return a.second > b.second;
		else return a.first > b.first;
	};
};

priority_queue<my_type, vector<my_type>, comp> pq;

 - 시간 복잡도

  1) 삽입

    * 배열/연결리스트 : O(n)

    *  : O(log2n)

  2) 삭제

    * 배열/연결리스트 : O(1)

    *  : O(log2n)

 

 

2) deque

 - #include <deque> 선언하여 사용

 - 주요 멤버 함수 : size(), empty(), at(), front(), back(), push_front(), push_back(), pop_front(), pop_back(), insert(), erase()

 - 시간 복잡도

    * at : O(1)

    * push_front : amortized O(1)

    * push_back : amortized O(1)

    * pop_front : amortized O(1)

    * pop_back : amortized O(1)

    * insert : O(n)

    * erase : O(n)

 

3) vector

 - #include <vector> 선언하여 사용

 - 주요 멤버 함수 : size(), empty(), at(), front(), back(), push_back(), pop_back(), insert(), erase()

 - 시간 복잡도

    * at : O(1)

    * push_back : amortized O(1)

    * pop_back : amortized O(1)

    * insert : O(n)

    * erase : O(n)

 - erase의 활용

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

using namespace std;

void show(vector<int>& v) {
	for (int i = 0; i < v.size(); i++) {
		cout << v[i] << " ";
	}
	cout << endl;
}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	// 초기화
	vector<int> v = { 0,0,4,0,0,0,0,4,0,0,4,0,4,4,4,0,0,0 };
	show(v);
	
	// 첫번째 4 제거
	v.erase(find(v.begin(), v.end(), 4)); // find()는 O(n)
	show(v);

	// 모든 4 제거
	v.erase(remove(v.begin(), v.end(), 4), v.end()); // remove()는 header file <algorithm> 필요
	show(v);

	return 0;
}

출력결과

0 0 4 0 0 0 0 4 0 0 4 0 4 4 4 0 0 0
0 0 0 0 0 0 4 0 0 4 0 4 4 4 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0

 

4) map

 - #include <map> 선언하여 사용

 - key, value 쌍으로 저장 : key에는 primitive type(int, long, char, double) 모두가 들어갈 수 있음
 - key 중복 불가 : multimap으로 key 중복 가능
 - 균형이진탐색트리(AVL트리) 구조 : 삽입/삭제 시마다 정렬 수행

 - 시간 복잡도

    * 검색/삽입/삭제 : O(logn)

    * 단, key가 크기가 m인 string 타입인 경우 : O(mlogn)

map<int, string> get_by_int;
map<string, int> get_by_string;

// 삽입
for (int i = 0; i < N; i++) {
    string temp; cin >> temp;
    get_by_int[i] = temp;
 	get_by_string[temp] = i;
}

// 검색
string str = get_by_int[245];
int num = get_by_string["key"];

 - 또 다른 insert 방법

get_by_string.insert(pair<string, int>("key", 27));

 - 상세한 사용법 (출처 : twpower.github.io/91-how-to-use-map-in-cpp)

    * iterator(반복자)

      → begin(): beginning iterator를 반환

      end(): end iterator를 반환

    * 추가 및 삭제

      insert(make_pair(key, value)): 맵에 원소를 pair 형태로 추가

       erase(key): 맵에서 key(키값)에 해당하는 원소 삭제

       clear(): 맵의 원소들 모두 삭제

    * 조회

       find(key): key(키값)에 해당하는 iterator를 반환

       count(key): key(키값)에 해당하는 원소들(value들)의 개수를 반환

    * 기타

       empty(): 맵이 비어있으면 true 아니면 false를 반환

       size(): 맵 원소들의 수를 반환

#include <iostream>
#include <string>
#include <map>

using namespace std;

int main() {
	map<string, int> m;

	m.insert(make_pair("a", 1));
	m.insert(make_pair("b", 2));
	m.insert(make_pair("c", 3));
	m.insert(make_pair("d", 4));
	m.insert(make_pair("e", 5));
	m["f"] = 6; // also possible

	m.erase("d");
	m.erase("e");
	m.erase(m.find("f")); // also possible

	if (!m.empty())
    	cout << "size: " << m.size() << '\n';

	cout << "a: " << m.find("a")->second << '\n';
	cout << "b: " << m.find("b")->second << '\n';

	cout << "a count: " << m.count("a") << '\n';
	cout << "b count: " << m.count("b") << '\n';

	cout << "traverse" << '\n';
	// map<string, int>::iterator it; also possible
	for (auto it = m.begin(); it != m.end(); it++) {
		cout << "key: " << it->first << " " << "value: " << it->second << '\n';
	}
    
	return 0;
}
// 출력 결과
size: 3
a: 1
b: 2
a count: 1
b count: 1
traverse
key: a value: 1
key: b value: 2
key: c value: 3

 - 특정 요소가 존재하는지 유무

if ( m.find("key") == m.end() ) {
  	// not found
} else {
  	// found
}

 - 중복 되는 key가 가능하도록 하고 싶을 때 : multimap 이용

#include <iostream>
#include <string>
#include <map>

using namespace std;

int main() {
	multimap<string, int> m;

	m.insert(make_pair("a", 1));
	m.insert(make_pair("a", 18));
	m.insert(make_pair("b", 2));
	m.insert(make_pair("c", 3));
	m.insert(make_pair("d", 4));
	m.insert(make_pair("e", 5));

	if (!m.empty())
		cout << "size: " << m.size() << '\n';

	cout << "a: " << m.find("a")->second << '\n';
	cout << "b: " << m.find("b")->second << '\n';

	cout << "a count: " << m.count("a") << '\n';
	cout << "b count: " << m.count("b") << '\n';

	cout << "traverse" << '\n';
	// map<string, int>::iterator it; also possible
	for (auto it = m.begin(); it != m.end(); it++) {
		cout << "key: " << it->first << " " << "value: " << it->second << '\n';
	}

	return 0;
}
size: 6
a: 1
b: 2
a count: 2
b count: 1
traverse
key: a value: 1
key: a value: 18
key: b value: 2
key: c value: 3
key: d value: 4
key: e value: 5

 - 하나의 key에 여러 value를 저장하고 싶을 때 : map<int, vector<int>> 와 같은 형식 사용

 

 

5) tuple

- pair 타입은 2개의 원소만 포함할 수 있지만, tuple은 3개 이상의 원소도 포함할 수 있다.

tuple<int, int, int> my_tuple = make_tuple(1, 2, 3);
cout << get<0>(my_tuple) << endl;
cout << get<1>(my_tuple) << endl;
cout << get<2>(my_tuple) << endl;

출력 결과

1
2
3

 - 참고 : www.crocus.co.kr/598

 


출처1 : https://plzrun.tistory.com/entry/삼성-SW검정-서티코딩취업준비-자주하는-질문들

출처2 : baactree.tistory.com/29

출처3 : stackoverflow.com/questions/22306949/does-deque-provide-o1-complexity-when-inserting-on-top

출처4 : blockdmask.tistory.com/366

출처5 : is03.tistory.com/65

출처6 : dawnarc.com/2019/09/algorithmstime-complexity-of-vector-set-and-map/

출처7 : https://blockdmask.tistory.com/335

 

'알고리즘 > 자료 구조' 카테고리의 다른 글

백준 15903번 : 카드 합체 놀이  (0) 2021.01.05
우선순위 큐와 힙 개념 및 C++ STL  (0) 2021.01.04
C++ STL vector 시간복잡도  (0) 2020.12.31
백준 1874번 : 스택 수열  (0) 2020.12.18
백준 10845번 : 큐  (0) 2020.12.17