BOJ 백준 1427 : 소트인사이드
Updated:
시작하면서
이번 문제는 퀵 소트 카테고리 문제이다. 이번 문제는 PS보다 퀵 소트 구현을 다시 해보고자 풀어본다.
개념
퀵 정렬(Quicksort)는 찰스 앤터니 리처도 호어가 개발한 정렬 알고리즘이다. 다른 원소와 비교만으로 정렬을 수행하는 비교 정렬에 속한다.
퀵 정렬은 n개의 데이터를 정렬할 때, 최악의 경우에는 O(n^2)번의 비교를 수행하고, 평균적으로 O(nlogn)번의 비교를 수행한다.
퀵 정렬의 내부 루프는 대부분의 컴퓨터 아키텍처에서 효율적으로 작동하도록 설계되어 있고(그 이유는 메모리 참조가 지역화되어 있기 때문에 CPU 캐시의 히트율이 높아지기 때문이다), 대부분의 실질적인 데이터를 정렬할 때 제곱 시간이 걸릴 확률이 거의 없도록 알고리즘을 설계하는 것이 가능하다.
또한, 매 단계에서 적어도 1개의 원소가 자기 자리를 찾게 되므로 이후 정렬할 개수가 줄어든다. 때문에 일반적인 경우 퀵 정렬은 다른 O(nlogn)알고리즘에 비해 훨씬 빠르게 동작한다. 그리고 퀵 정렬을 정렬을 위해 O(logn)만큼의 memory를 필요로 한다.
구현
퀵 정렬은 분할 정복(divide and conquer) 방법을 통해 리스트를 정렬한다.
- 리스트에서 하나의 원소를 고른다. 이렇게 고른 원소를 피벗이라고 한다.
- 피벗 앞에는 피벗보다 값이 작은 모든 원소들이 오고, 피벗 뒤에는 피벗보다 값이 큰 모든 원소들이 오도록 피벗을 기준으로 리스트를 둘로 나눈다. 이렇게 리스트를 둘로 나누는 것을 분할이라고 한다. 분할을 마친 뒤에 피벗은 더 이상 움직이지 않는다.
- 분할된 두 개의 작은 리스트에 대해 재귀(Recursion)적으로 이 과정을 반복한다. 재귀는 리스트의 크기가 0이나 1이 될 때까지 반복된다.
재귀 호출이 한 번 진행될 때마다 최소한 하나의 원소는 최종적으로 위치가 정해지므로, 이 알고리즘은 반드시 끝난다는 것을 보장할 수 있다.
문제 이해
해당 문제는 이해할 내용이 어렵지 않다. 수를 받아서, 해당 숫자 자릿수들을 내림차순으로 정렬해서 출력하면 된다.
문제 아이디어
input은 string으로 받고, 해당 string을 int 배열로 변경해서 문제를 풀고자 한다.
해결
#include <iostream>
#include <string>
using namespace std;
string Input();
void MakeArray(string input,int *arr, int *size);
void Swap(int *a, int *b);
void QuickSort(int *arr, int start, int end);
void PrintAnswer(int *arr, int size);
int main()
{
// sync off
ios::sync_with_stdio(false);
cin.tie(0);
string input = "";
int arr[10] = { 0, };
int size = 0;
input = Input();
MakeArray(input, arr, &size);
QuickSort(arr, 0, size - 1);
PrintAnswer(arr, size);
}
string Input()
{
string input;
cin >> input;
return input;
}
void MakeArray(string input, int *arr, int *size)
{
*size = input.size();
for (int i = 0; i < input.size(); i++)
{
arr[i] = input[i] - '0';
}
}
void Swap(int *a, int *b)
{
int c;
c = *a;
*a = *b;
*b = c;
}
void QuickSort(int *arr, int start, int end)
{
if (start >= end)
{
return;
}
int pivot = start;
int left = start + 1;
int right = end;
while (left <= right)
{
while (left <= end && arr[left] >= arr[pivot])
{
left++;
}
while (right > start && arr[right] <= arr[pivot])
{
right--;
}
if (left > right)
{
Swap(&arr[pivot], &arr[right]);
}
else
{
Swap(&arr[left], &arr[right]);
}
}
QuickSort(arr, start, right - 1);
QuickSort(arr, right + 1, end);
}
void PrintAnswer(int *arr, int size)
{
for (int i = 0; i < size; i++)
{
cout << arr[i];
}
}
퀵 정렬을 만들 때 주의해야할 점은, 중복된 값이 있을 때 처리해주어야 한다.
...
while (left <= end && arr[left] >= arr[pivot])
{
left++;
}
while (right > start && arr[right] <= arr[pivot])
{
right--;
}
...
해당 코드에서 left, right와 pivot의 값을 비교할 때 값이 같을 경우를 넣어주지 않으면 비교가 불가능한 경우가 생겨서 시간초과가 발생한다. 주의해야한다.
마치며
퀵 정렬은 취업 준비할 때 필수였기 때문에 여러번 만들고 공부했던 부분이었다. 하지만 오랜만에 코드로 다시 짜보니, 머리에 지식은 있어도 예외 처리가 잘 되지 않았다. 복습하기 참 잘했다고 생각한다.
Leave a comment