개발 지식/알고리즘
쉘 정렬
JUTABI
2020. 7. 9. 04:58
JAVA로 작성되었습니다.
쉘 정렬
- 삽입 정렬을 개산한 알고리즘, 바탕은 삽입정렬을 기본으로 진행한다.
- 가까운 위치에 삽입되는 (어느정도 정렬이 되어있는) 삽입정렬이 빠른 것을 이용하여 삽입정렬을 하기 전 배열을 어느정도 정렬한 상태에서 삽입정렬을 진행하는정렬 방식이다.
- 이때 gap이라는 것이 존재하는데 gap만큼 배열을 띄엄띄엄 정렬하는데 사용한다.
- 갭을 설정한다. 갭의 값은 이전 갭의 절반으로 설정한다.
(초기 값은 배열의 크기 / 2 로 설정한다)
(홀수를 권장하므로 2로 나누었을 때 짝수가 나온다면 +1을 하여 홀수로 정한다.) - 원본 배열에서 다음 요소와 인덱스의 차이가 gap만큼 있는 부분 배열들을 생성한다.
(리스트의 길이가 11(0~10)이고 갭이 5라면
(0, 5, 10), (1, 6), (2, 7), (3, 8), (4, 9)) - 부분 배열들을 각각 삽입정렬해준다.
- 각각의 부분들의 정렬이 완료되면 다음 갭 (나누기 2)으로 부분 삽입정렬을 다시 실행한다.
- 갭이 1이 될 때까지 정렬을 진행한다. ( = 어느정도 정렬된 배열의 삽입정렬)
정렬 시각화
- 1
6 | 2 | 4 | 0 | 2 | 5 | 1 | 8 | 7 | 4 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
6 | 5 | 10 | ||||||||
2 | 1 | |||||||||
4 | 8 | |||||||||
0 | 7 |
->
5 | 1 | 4 | 0 | 2 | 6 | 2 | 8 | 7 | 4 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
5 | 6 | 10 | ||||||||
1 | 2 | |||||||||
4 | 8 | |||||||||
0 | 7 | |||||||||
2 | 4 |
- 2
5 | 1 | 4 | 0 | 2 | 6 | 2 | 8 | 7 | 4 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
5 | 0 | 2 | 4 | |||||||
1 | 2 | 8 | 10 | |||||||
4 | 6 | 7 |
->
0 | 1 | 4 | 2 | 2 | 6 | 4 | 8 | 7 | 5 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
0 | 2 | 4 | 5 | |||||||
1 | 2 | 8 | 10 | |||||||
4 | 6 | 7 |
- 3
0 | 1 | 4 | 2 | 2 | 6 | 4 | 8 | 7 | 5 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 2 | 2 | 6 | 4 | 8 | 7 | 5 | 10 |
->
0 | 1 | 2 | 2 | 4 | 4 | 5 | 6 | 7 | 8 | 10 |
---|
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class ShellSort {
public int[] array_selection(int[] arr, int gap, int start_index) {
for (int i = start_index + gap; i < arr.length; i += gap) {
int j, value = arr[i];
for (j = i - gap; j >= start_index && arr[j] > value; j -= gap) {
arr[j + gap] = arr[j];
}
arr[j + gap] = value;
}
return arr;
}
public int[] array_shell(int[] arr) {
for (int gap = arr.length / 2; gap > 0; gap = gap / 2) {
if (gap % 2 == 0) { gap++; }
for (int i = 0; i < gap; i++) {
ShellSort shellSort = new ShellSort();
arr = shellSort.array_selection(arr, gap, i);
}
}
return arr;
}
public List<Integer> list_shell(List<Integer> list, int prev_gap) {
// 이전 gap의 절반을 gap으로 설정, 짝수가 나오면 +1을 하여 홀수로 설정
int gap = (prev_gap % 2 == 0) ? prev_gap / 2 + 1 : prev_gap / 2;
// 0부터 +gap 한 애들 삽입정렬
// 그렇다면 갭의 크기만큼 반복문이 있겠지?
for (int i = 0; i < gap; i++) {
// 리스트의 길이가 10(0~9)이고 갭이 5라면
// (0, 5), (1, 6), (2, 7), (3, 8), (4, 9) 다섯번 반복하는 삽입정렬
// ex) 0과 5를 삽입정렬 -> 1과 6을 삽입정렬 -> ...
for (int j = i + gap; j < list.size(); j += gap) {
int k, value = list.get(j);
for (k = j - gap; k >= i && list.get(k) > value; k -= gap) {
list.set(k + gap, list.get(k));
}
list.set(k + gap, value);
}
}
if (gap != 1) {
list = list_shell(list, gap);
}
return list;
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
Random random = new Random();
ShellSort shellSort = new ShellSort();
ListAccuracy listAccuracy = new ListAccuracy();
int[] arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = random.nextInt(10);
}
System.out.print("before: ");
listAccuracy.array_print(arr);
shellSort.array_shell(arr);
System.out.print("after: ");
listAccuracy.array_print(arr);
for (int i = 0; i < 10; i++) {
list.add(random.nextInt(10));
}
List<Integer> origin_copy = new ArrayList<Integer>(list);
System.out.println("before: " + list);
list = shellSort.list_shell(list, list.size());
System.out.println("after: " + list);
// 윈본과 일치율을 확인하는 코드가 작성되어있다.
// 합병정렬에서 구현한 코드와 동일하다.
listAccuracy.accuracy_test(origin_copy, list);
}
}