Algorithm/[Do it! 알고리즘 코딩테스트 with C++] 5장. 정렬
6개의 기본 정렬 알고리즘 학습하기
5-0 각 정렬 알고리즘의 정의
| 알고리즘 | 정의 |
|---|---|
| 버블 | 데이터의 인접 요소끼리 비교하고, swap 연산을 수행하며 정렬하는 방식 |
| 선택 | 대상에서 가장 크거나 작은 데이터를 찾아 선택하는 과정을 반복하면서 정렬하는 방식 |
| 삽입 | 대상을 선택해 정렬된 영역에서 선택 데이터의 적절한 위치를 찾아 삽입하면서 정렬하는 방식 |
| 퀵 | 피벗 값을 선정해 해당 값을 기준으로 정렬하는 방식 |
| 병합 | 이미 정렬된 부분 집합들을 효율적으로 병합해 전체를 정렬하는 방식 |
| 기수 | 데이터의 자릿수를 바탕으로 비교해 데이터를 정렬하는 방식 |
5-1 버블 정렬
버블정렬은 두 인접한 데이터의 크기를 비교해 정렬하는 방법이다.
간단하게 구현할 수 있지만 시간복잡도는 O(n^2)으로, 다른 정렬 알고리즘보다 속도가 느린 편이다.
다음과 같이 루프를 돌면서 인접한 데이터 간의 swap 연산으로 정렬한다.
버블 정렬 과정
- 비교 연산이 필요한 루프 범위를 설정한다.
- 인접한 데이터 값을 비교한다.
- swap 조건에 부합하면 swap 연산을 수행한다.
- 루프 범위가 끝날 때까지 2~3을 반복한다.
- 정렬된 영역을 설정한다. 다음 루프를 실행할때는 이 영역을 제외한다.
- 비교 대상이 없을 때까지 1~5를 반복한다
그리고 만약 특정한 루프의 전체 영역에서 swap 이 한번도 발생하지 않았다면 그 영역 뒤에 있는 데이터는 모두 정렬되었다는 뜻이므로 루프를 종료해도 된다.
문제 풀이
[문제 015] 수 정렬하기 1
시간 제한 1초, 브론즈 2, 백준 2750번
N개의 수가 주어졌을 때 이를 오름차순 정렬하는 프로그램을 작성하시오
sort() 함수를 사용해서 쉽게 정렬할 수 있지만, 정렬을 직접 구현해 해결해보는 문제다.
N의 최대 범위는 1,000으로 주어져있어 매우 작은데 비해 시간 제한은 1초로 꽤 널널하다. 따라서 O(n^2)의 시간복잡도로 문제를 풀어도 최대 연산 100만번, 1억 이내로는 매우 안정적이다.
의사코드 작성하기
N(정렬할 수 개수)
A(저장할 배열)
for (i는 0부터 N-1까지) {
for (j는 0부터 N-1-i까지) {
오름차순이니까 현재 A[j] > A[j+1] 이면 swap
}
}
A 출력
풀이 코드
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int N;
cin >> N;
vector<int> A(N, 0);
for (int i = 0; i < N; i++) {
cin >> A[i];
}
for (int i = 0; i < N - 1; i++) {
for (int j = 0; j < N - 1 - i; j++) {
if(A[j] > A[j + 1]) {
int temp = A[j];
A[j] = A[j + 1];
A[j + 1] = temp;
}
}
}
for (int i = 0; i < N; i++) {
cout << A[i] << "\n";
}
return 0;
}
5-2 선택 정렬
선택 정렬은 대상 데이터에서 최대나 최소 데이터를 나열된 순으로 찾아가며 선책하는 방법이다.
구현 방법이 비교적 복잡하고, 시간 복잡도도 O(n^2)으로 효율적이지는 않아 코테에서 많이 사용하지 않는 방법이긴 하다.
최솟값 혹은 최댓값을 찾고, 남은 정렬 부분의 가장 앞에 있는 값과 swap 하는 것이 핵심 아이디어다!
선택 정렬 과정
- 남은 정렬 부분에서 최솟값 혹은 최댓값을 찾는다.
- 남은 정렬 부분에서 가장 앞에 있는 값과 선택된 데이터를 swap 한다.
- 가장 앞에 있는 데이터의 위치를 변경해(index++) 남은 정렬 부분의 범위를 축소한다.
- 전체 데이터 크기만큼 index 가 커질 때까지, 즉 남은 정렬 부분이 없을 때까지 반복한다.
5-3 삽입 정렬
삽입 정렬은 이미 정렬된 데이터 범위에 정렬되지 않은 데이터를 적절한 위치에 삽입해 정렬하는 방식이다.
마찬가지로 시간 복잡도는 O(n^2)로 느린 편이지만 구현하기가 쉽다.
선택한 값을 현재 정렬된 범위 내에서 적절한 위치에 삽입하는 것이 핵심 아이디어다.
삽입 정렬 과정
- 현재 인덱스에 있는 값을 선택한다.
- 정렬된 범위에서 현재 선택한 값이 삽입될 위치를 탐색한다.
- 삽입 위치부터 인덱스에 있는 위치까지 shift 연산을 수행한다.
- 삽입 위치에 현재 선택한 값을 삽입하고 index++ 연산을 수행한다.
- 전체 데이터의 크기만큼 index가 커질 때까지, 즉 더이상 선택할 값이 없을 때까지 반복한다.
5-4 퀵 정렬
퀵 정렬은 기준값 pivot을 선정해 해당 값보다 작은 값과 큰 값으로 분류하는 것을 반복해 정렬하는 알고리즘이다.
피벗을 어떻게 선정하는지가 시간 복잡도에 많은 영향을 미치는데, 평균 시간복잡도는 O(nlogn) 이며 최악의 경우에는 O(n^2)이다.
피벗을 중심으로 계속 데이터를 2개의 집합으로 나누면서 정렬하는 것이 핵심 아이디어다!