전에 작성했던 게시물에 전반적으로 어떤 정렬 알고리즘이 있는지 알아봤습니다! 다만, 그렇게 지나가기에는 다소 이해도가 부족한 감이 있어 이를 보충하기 위해서 다양한 알고리즘을 한층 더 알아보고 응용 문제를 풀어보고 이해하는 시간을 가지려고 합니다.
Counting Sort
는 O(n)의 시간복잡도를 가진 알고리즘입니다. 기존의 Heap Sort, Quick Sort와 같이 O(nlogn)의 시간복잡도를 가지는 것들과 비교를 했을 경우 상당히 빠르다는 것을 알 수 있습니다. 이렇게 차이가 나는 것은 Quick, Heap Sorting 같은 경우는 직접 비교를 하는 경우가 많습니다. 그렇기 때문에 속도가 O(nlogn)보다 작아질 수 없는 것 입니다.
대부분의 배열은 이 과정을 거치게 되었을 때 정렬이 되어 출력을 하면됩니다. 하지만, Input 값이 자연수가 아니고 소수의 경우에는 구분이 정화갛게 진행되지 않기 때문에 추가적인 작업을 진행합니다.(만약 소수의 값도 포함해서 정렬을 해야 할 경우에는 Counting Sort가 아닌 Array.sort 등의 다른 알고리즘을 통한 정렬 방식을 사용해야 합니다.)
// Read the number of times
int nTimes = Integer.parseInt(br.readLine());
// Use an array to store frequencies, considering the constraint (<= 10000)
int[] frequencyArray = new int[10001];
// Read the numbers into the array and count frequencies
for (int i = 0; i < nTimes; i++) {
int number = Integer.parseInt(br.readLine());
frequencyArray[number]++;
}
이렇게 누적합을 통해서 우리는 본래 배열(input)의 값을 타고 가 counting array의 인덱스로 접근을 하면 Counting array
의 key 값에 -1을 한 값이 정렬 결과(output array)를 나타낸다는 것을 알 수 있습니다.(누적합 array를 만듦으로써, 순서가 지정된 시퀸스를 형성합니다.)
// Step 2
for (int i = 1; i < countArray.length; i++) {
countArray[i] += countArray[i - 1];
}
실제 문제를 푸는 과정에서는 사용하지 않았지만, 정석적인 방법에서는 이와 같이 하는 것이 맞다고 합니다😚 그래서 위의 코드와 약간 맞지 않는 부분이 있을 수 있으나, 이러한 방법이 있다는 것을 보여드리기 위해 코드를 같이 첨부합니다!
이렇게 위와 같이 정렬이 될 수 있습니다. 그 외에도 현재 예시에는 없지만, 순차적으로 숫자가 나열되어 있지 않고 비어 있는 경우에도 Counting array의 범위가 조금 커지지만 결과를 출력함에 있어 같이 과정을 밟게 되면 올바른 결과를 얻을 수 있습니다.
// Use StringBuilder to buffer the output
StringBuilder output = new StringBuilder();
for (int number = 1; number <= 10000; number++) {
for (int frequency = 0; frequency < frequencyArray[number]; frequency++) {
output.append(number).append('\n');
}
}
// Print the final output
System.out.print(output);
여기서 메모리 조건이 없는 경우에는 System.out.println
을 사용해도 무방합니다. 다만, StringBuilder
를 사용한 이유는 다음과 같습니다.
StringBuilder
는 가변적으로 설계되어 내부의 내용을 변경할 수 있습니다. 그렇기 때문에 Loop 내에서 새로운 객체를 생성하지 않고 내용을 수정할 수 있습니다.(동적할당 가능)위와 같은 이유로 StringBuilder를 사용하여 효율적으로 Sorting을 할 수 있습니다.
Selection Sort
는 이름 그대로의 성질을 가지고 있습니다. Input array의 각 값을 비교해 가면서 해당 인덱스에 들어갈 데이터를 찾아 선택하는 알고리즘입니다.
해당 알고리즘을 정렬하는 방법은 아래와 같습니다.
조금 더 직관적으로 이해를 위해서는 아래와 같다고 생각할 수 있습니다.
방금 말씀드린 것 처럼 비교 정렬이며, in-place sort를 하고 있기 때문에 추가적인 메모리를 필요로 하지 않습니다. 그럼 Selection Sort를 구현 해보겠습니다.
public class SelectionSort{
public static void SelectionSort(int[] arr){
int n = arr.length;
for(int i = 0; i < n-1; i++){
int minIndex = i;
for(int j = i+1; j< n; j++){
if(arr[j] < arr[minIndex]){
minIndex = j;
}
}
swap(arr, minIndex, i);
}
}
public static void swap(int[] a, int i, int j){
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
위와 같이 구현할 수 있습니다. 원래 swap
이라는 class는 c++에서 두 수를 비교하기 위해서 존재하지만, 자바에는 존재하지 않기 때문에 따로 만들어서 사용합니다.
장점은 위에서 이미 언급을 했기 때문에 차치하고 단점에 대해 설명드리겠습니다. Selection Sort는 최솟값을 찾아야 하기 때문에 Input array를 한번 탐색하고 해당 자리와 비교하느 숫자를 찾고 인덱스를 swap하는 과정에서 한번 더 탐색을 하기 때문에 이와 같은 시간 복잡도를 가지게 됩니다.
또한, Stable Sort가 아니라는 것은 정렬하는 결과가 매번 달라질 수 있다는 것을 의미합니다. 예를들어 학생 테이블의 성적을 정리한 테이블을 정렬한다고 했을 때, 동점자가 나온 경우 이름 순으로 정렬을 해야 합니다. 이때, Selection Sort
를 사용하게 되면 처음에 정렬한 점수에 대한 정렬만 고려가 되고 이름에 대한 정렬은 이뤄지지 않고 그 해당 결과가 매번 다르게 나올 수 있습니다.
Insertion Sort
는 단순하게 우리 수중에 있는 카드를 정렬하는 것과 유사한 방법으로 Input array를 정렬하는 알고리즘입니다. 배열을 우선 정렬부와 정렬하지 않은 부분으로 나누고 정렬이 안된 부분에 대한 값을 사이사이 맞는 공간에 삽입합니다. 기본적으로 Ascending order로 정렬을 하게 됩니다.
해당 정렬 알고리즘도 Selection Sort
와 비슷하게 비교 정렬이며, in-place sort이기도 합니다. 하지만 이전의 Selction sort와는 다르게 stable sort입니다.
public static void insertionSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; ++i) {
int key = arr[i];
int j = i - 1;
// Move elements of arr[0..i-1] that are greater than key
// to one position ahead of their current position
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
I thought of putting in the class of
swap
to while loop instead but it doesn't satisfy to control to work this code well. that looked too messy.
결과적으로 while문의 코드를 통해서 j를 1씩 줄여가며 만약에 앞에 있는 수들 보다 현재 타켓이 작을 경우 앞으로 보내기 위해서 코드가 구성되어 있습니다. 그래서 이전 원소가 타겟 숫자보다 크기 직전까지 모든 수를 뒤로 한 칸씩 밀어내는 것과 같습니다.
// Java program for implementation of Insertion Sort
public class InsertionSort {
/*Function to sort array using insertion sort*/
void sort(int arr[])
{
int n = arr.length;
for (int i = 1; i < n; ++i) {
int key = arr[i];
int j = i - 1;
/* Move elements of arr[0..i-1], that are
greater than key, to one position ahead
of their current position */
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
/* A utility function to print array of size n*/
static void printArray(int arr[])
{
int n = arr.length;
for (int i = 0; i < n; ++i)
System.out.print(arr[i] + " ");
System.out.println();
}
// Driver method
public static void main(String args[])
{
int arr[] = { 12, 11, 13, 5, 6 };
InsertionSort ob = new InsertionSort();
ob.sort(arr);
printArray(arr);
}
};
references
1. Sorting algorithms
2. Insertion Sort