버블 정렬은 loop를 돌며 데이터의 인접 요소끼리 비교하고, swap 연산을 수행하면 정렬하는 방식이다. 간단하게 구현할 수 있지만, 시간 복잡도가 O(n²)으로 다른 정렬 알고리즘보다 속도가 느린 편이다.
만약 특정한 루프의 전체 영역에서 swap이 한 번도 발생하지 않았다면 그 영역 뒤에 있는 데이터가 모두 정렬되었다는 뜻이 되며, 프로세스를 종료해도 된다.
import java.util.*;
class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (a[j] > a[j + 1]) {
int num = a[j];
a[j] = a[j + 1];
a[j + 1] = num;
}
}
}
for (int i = 0; i < n; i++) {
System.out.println(a[i]);
}
}
}
선택 정렬은 대상 데이터에서 최대나 최소 데이터를 데이터가 나열된 순으로 찾아가며 선택하는 방법이다. 구현 방법이 복잡하고, 시간 복잡도도 O(n²)으로 비효율적이다.
import java.util.*;
class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.next();
int[] a = new int[str.length()];
for (int i = 0; i < str.length(); i++) {
a[i] = Integer.parseInt(str.substring(i, i+1));
}
for (int i = 0; i < a.length; i++) {
int max = i;
for (int j = i + 1; j < a.length; j++) {
if (a[max] < a[j]) {
max = j;
}
}
if (a[i] < a[max]) {
int num = a[i];
a[i] = a[max];
a[max] = num;
}
}
for (int i = 0; i < a.length; i++) {
System.out.print(a[i]);
}
}
}
이미 정렬된 데이터 범위에 정렬되지 않은 데이터를 적절한 위치에 삽입시켜 정렬하는 방식으로 O(n²)의 시간 복잡도로 느린 편이지만 구현이 쉽다. 선택 데이터를 현재 정렬된 데이터 범위 내 적절한 위치에 삽입하는 것이 핵심이다.
기준값을 선정해 해당 값보다 작은 데이터와 큰 데이터로 분류하는 것을 반복하여 정렬한다. 기준값(pivot) 선정 기준이 시간 복잡도에 많은 영향을 미치며, 평균 시간 복잡도는 O(nlogn)이며, 최악의 경우 O(n²)이 된다.
분할 정복 방식을 사용해 데이터를 분할하고, 분할한 집합을 정렬하며 합치는 정렬방법이며, 시간 복잡도는 O(nlogn)이다.
투 포인터 개념을 사용하여 두 그룹을 병합한다. 왼쪽 포인터(i)와 오른쪽 포인터(j)의 값을 비교하여 작은 값을 결과 배열에 추가하고 포인터를 오른쪽으로 이동시킨다.
public class MergeSort {
public static void main(String[] args) {
int[] arr = { 9, 7, 3, 1, 6, 2, 8, 4, 5 };
mergeSort(arr, 0, arr.length - 1);
for (int i : arr) {
System.out.print(i + " ");
}
}
public static void mergeSort(int[] arr, int left, int right) {
if (left < right) {
int mid = (left + right) / 2; // 중간 지점 계산
mergeSort(arr, left, mid); // 왼쪽 반 정렬
mergeSort(arr, mid + 1, right);// 오른쪽 반 정렬
merge(arr, left, mid, right); // 정렬된 반들을 병합
}
}
public static void merge(int[] arr, int left, int mid, int right) {
int[] tempArr = new int[right - left + 1]; // 병합을 위한 임시 배열 생성
int i = left, j = mid + 1, k = 0;
while (i <= mid && j <= right) { // 병합
if (arr[i] <= arr[j]) {
tempArr[k++] = arr[i++];
} else {
tempArr[k++] = arr[j++];
}
}
// 왼쪽 반 나머지 항목들 복사
while (i <= mid) {
tempArr[k++] = arr[i++];
}
// 오른쪽 반 나머지 항목들 복사
while (j <= right) {
tempArr[k++] = arr[j++];
}
// 임시 배열을 복사하여 정렬된 배열로 업데이트
for (i = left, k = 0; i <= right; i++, k++) {
arr[i] = tempArr[k];
}
}
}
값을 비교하지 않고 비교할 자릿수를 정한 후 해당 자릿수만 비교하는 정렬 방식으로 시간 복잡도는 O(kn)이다. k는 데이터의 자릿수를 의미한다.
기수 정렬은 10개의 큐를 이용(0 ~ 9의 수를 담기 위해)하며, 각 큐는 값의 자릴수를 대표한다.