방학기간 동안 알고리즘을 공부하면서 알고리즘별로 배웠던 내용을 정리하면 좋을 거 같아 글을 쓰게 되었다. 내용은 바킹독님의 강의를 토대로 정리하였으며 추후 내용이 추가될 예정이다.
메모리 상에 원소를 연속하게 배치한 자료구조
원소들을 저장할 때 그 다음 원소가 있는 위치를 포함시키는 방식으로 저장하는 자료구조
연결 리스트의 종류
한쪽 끝에서만 원소를 넣거나 뺄 수 있는 자료구조
스택이 비어있는데 top을 호출하거나 pop을 호출하면 런타임 에러가 발생함에 주의하자.
한쪽 끝에서 원소를 넣고 반대쪽 끝에서 원소를 뺄 수 있는 자료구조
스택과는 다르게 큐를 배열로 구현하면 삭제가 발생할 때마다 앞쪽에 쓸모없는 공간이 계속 생기게 된다. 이 문제를 해결하는 방법은 큐의 원소가 들어갈 배열을 원형으로 만드는 것이다. 마지막 index에서 다음 index로 갈 때 0번지로 다시 오도록 하면 되는데 이러한 큐를 원형 큐(Circular Queue)라고 부른다.
큐가 비어있는데 front, back, pop을 호출하면 런타임 에러가 발생함에 주의하자.
양쪽 끝에서 삽입과 삭제가 전부 가능한 자료구조
제일 앞/뒤가 아닌 나머지 원소들의 확인/변경이 원칙적으로는 불가능하지만 STL deque에서는 인덱스로 원소에 접근할 수 있다.
앞쪽과 뒷쪽에서의 추가와 제거가 모두 필요하면 당연히 STL deque을 사용하는 게 효율적이지만 굳이 앞쪽에서의 추가와 제거가 필요하지 않고 배열과 같은 느낌으로 쓰고싶을 땐 STL deque말고 STL vector를 쓰면 된다.
다차원 배열에서 각 칸을 방문할 때 너비를 우선으로 방문하는 알고리즘
자주 실수하는 점들을 짚어보면,
다차원 배열에서 각 칸을 방문할 때 깊이를 우선으로 방문하는 알고리즘
Flood Fill
은 BFS와 DFS 중에서 어느 것을 써도 상관없지만 거리 측정은 BFS만 할 수 있으니 DFS를 쓸 일이 없다. 그래프와 트리라는 자료구조를 배울 때 DFS가 필요하다.
하나의 함수에서 자기 자신을 다시 호출해 작업을 수행하는 알고리즘
재귀 함수의 조건? 특정 입력에 대해서는 자기 자신을 호출하지 않고 종료되어야 함(Base condition), 모든 입력은 base condition으로 수렴해야 함
재귀에 대한 정보
재귀로 풀어야 하는 문제라면 다음 3가지 Step을 기억하자.
현재 상태에서 가능한 모든 후보군을 따라 들어가며 탐색하는 알고리즘
보통 재귀적으로 구현이 된다.
BFS나 재귀와 같이 특정 자료구조 혹은 알고리즘에 종속되지 않고 주어진 문제 상황을 구현하면 되는데 이때 구현이 빡세게 필요한 것들을 통틀어서 시뮬레이션 유형의 문제라고 한다.
앞에서부터 인접한 두 원소를 보면서 앞의 원소가 뒤의 원소보다 클 경우 자리를 바꾸는 것을 반복하여 정렬한다.
시간복잡도는 O(N^2)이다.
재귀적으로 수열을 나눠 정렬한 후 합치는 정렬이다.
시간복잡도는 O(NlgN)이다.
우선 순위가 같은 원소들끼리는 원래의 순서를 따라가도록 하는 정렬이 Stable Sort
라고 하는데 Merge Sort가 이에 속한다.
이름에서도 알 수 있듯이 일반적으로는 Quick Sort가 거의 모든 정렬 알고리즘보다 빨라서 각종 라이브러리의 정렬은 대부분 Quick Sort를 바탕으로 만들어졌다.
하지만, 기억해야할 점은 코딩테스트에서 STL을 못 쓰고 직접 정렬을 구현해야 한다면 Quick Sort는 절대 쓰면 안된다는 것이다.
Merge Sort처럼 재귀적으로 구현되는 정렬인데 매 단계마다 pivot이라고 이름 붙은 원소 하나를 제자리로 보내는 작업을 반복한다.
평균적으로 시간복잡도는 O(NlgN)이지만 최악의 경우 O(N^2)이다. 이러한 이유로 직접 정렬을 구현해야 한다면 Quick Sort를 절대 쓰면 안된다는 것이다.
미리 큰 테이블을 만들어두고 수에 대응되는 원소의 값을 1 증가시켜서 정렬한다.
하지만 만약 수의 범위가 0에서 999,999,999 까지라고 하면 크기가 10억인 배열이 필요하므로 수의 범위가 어느 정도 한정적일 때에만 쓸 수 있다. 수의 범위가 대략 1000만 이하일 때에는 쓸 수 있다고 생각하면 된다.
자릿수를 이용해서 정렬을 수행하는 알고리즘으로 Counting Sort를 응용한 알고리즘이라고 생각할 수 있다.
각 수들의 1의 자리만 보고 정렬을 하고, 그 후에는 10의 자리만 보고 정렬, 그 후에는 100의 자리, ... 이렇게 정렬을 수행한다.
Bubble Sort, Merge Sort, Quick Sort는 원소들끼리 크기를 비교하므로 Comparison Sort이고 Counting Sort, Radix Sort는 Non-comparison Sort이다.
여러개의 하위 문제를 먼저 푼 후 그 결과를 쌓아올려 주어진 문제를 해결하는 알고리즘
쉽게 설명하면 문제를 해결하기 위한 점화식을 찾아낸 후 점화식의 항을 밑에서부터 차례로 구해나가서 답을 알아내는 형태의 알고리즘을 말한다.
DP를 푸는 과정
지금 가장 최적인 답을 근시안적으로 택하는 알고리즘
= 관찰을 통해 탐색 범위를 줄이는 알고리즘
이상적인 풀이 흐름
현실적인 풀이 흐름
절망적인 풀이 흐름
코딩테스트에서의 추천 전략
정렬되어 있는 배열에서 특정 데이터를 찾기 위해 모든 데이터를 순차적으로 확인하는 대신 탐색 범위를 절반으로 줄여가며 찾는 탐색 방법
STL을 쓰지 않고 구현을 한다면 다음을 주의해야 한다.
주의사항
1. 이분탐색을 하고자 한다면 주어진 배열은 정렬되어 있어야 한다.
2. 무한 루프에 빠지지 않게 mid 값을 정해야 한다.
중복 제거를 수행해주는 STL인 unique라는 함수가 있다. 우선 unique는 정렬된 배열에서 실행을 시켜야 하고, 중복이 제거된 원소들을 앞으로 모아준다. 뒤쪽에는 쓰레기 값들이 들어가고 쓰레기 값이 시작되는 구간을 return하는데 vector의 erase를 이용해서 뒤쪽을 날리면 된다.
vector<int> uni;
:
sort(uni.begin(), uni.end());
uni.erase(unique(uni.begin(), uni.end()), uni.end());
조건을 만족하는 최소/최댓값을 구하는 문제(최적화 문제)를 결정 문제로 변환해 이분탐색을 수행하는 방법
이분탐색을 수행할 변수를 가지고 함수를 세웠을 때 그 함수가 감소함수거나 증가함수여야 한다.