안녕하세요, 여행벌입니다.

오늘은 셸정렬(shell sort)에 대해서 포스팅해보겠습니다.


셸 정렬의 원리

 셸 정렬(shell sort)은 Donald L. Shell 이라는 사람이 제안한 방법으로 삽입 정렬이 어느 정도 정렬된 배열에 대해서는 대단히 빠르게 정렬한다는 사실에 착안한 방법입니다.

 

 삽입 정렬의 최대 문제점은 요소들이 삽입될 때, 이웃한 위치로만 이동하기 때문에 삽입되어야 할 위치가 현재 위치에서 상당히 멀리 떨어진 곳이라면 많은 이동을 해야 합니다. 셸 정렬에서는 바로 이웃한 위치로만 이동하는 게 아니라 멀리 떨어진 위치로도 이동을 할 수 있습니다. 

 

 삽입 정렬과는 다르게 셸 정렬은 전체의 리스트를 한 번에 정렬하지 않습니다. 대신에 먼저 정렬해야 할 리스트를 일정한 기준에 따라 분류하여 연속적이지 않은 여러 개의 부분 리스트를 만들고, 각 부분 리스트를 삽입 정렬을 이용하여 정렬합니다. 모든 부분 리스트가 정렬되면 셸 정렬은 다시 전체 리스트를 더 적은 개수의 부분 리스트로 만든 후에 이 과정을 되풀이합니다.

 

 부분 리스트를 구성할 때는 주어진 리스트의 각 K 번째 요소를 추출하여 만들고, 이 K를 간격(gap)이라고 합니다.

이 K를 줄여가며 수행 과정을 반복합니다.

 

 셸 정렬은 삽입 정렬을 보완할 정렬로 삽입 정렬에 비해 2가지 장점이 있습니다.

 

 1. 연속적이지 않은 부분 리스트에서 자료의 교환이 일어나면 더 큰 거리를 이동하므로 교환되는 요소들이 삽입 정렬보다는 최종 위치에 더 가까이 있을 가능성이 높아진다.

 2. 부분 리스트는 어느 정도 정렬이 된 상태이기 때문에 부분 리스트의 개수가 1이 되었을 때, 삽입 정렬이 훨씬 빠르게 수행된다.


셸 정렬 예시

( 10 2 5 4 1 6 9 8 7 3 ) 리스트를 셸 정렬을 이용해서 정렬해보겠습니다.

처음의 K(gap) = N / 2 = 5 입니다.

K가 5이므로 5칸 떨어진 요소들과 부분 리스트를 이루게 됩니다.

10과 6이 같은 부분 리스트이므로 6과 10으로 삽입 정렬을 통해 정렬됩니다.

2와 9 / 5와 8 / 4 와 7 / 1 과 3도 각각 같은 부분 리스트에 속하지만 정렬이 되어있으므로 삽입 정렬을 수행하지 않습니다.

 

이제 K(gap)을 반으로 줄입니다.

K가 2이므로 2칸 떨어진 요소들과 부분 리스트를 이루게 됩니다.

6 - 5 - 1 - 9 - 7 / 2 - 4 - 10 - 8 - 3 이 각각 부분 리스트를 이루게 되고,

각각 삽입 정렬을 통해 정렬하게 됩니다.

 

또, K(gap)을 반으로 줄입니다.

K가 1이므로 1칸 떨어진 요소, 즉 전체 리스트에 대해서 삽입 정렬을 수행합니다.

모든 과정을 표로 표현하면 다음과 같습니다.


셸 정렬 코드

 간격은 처음에는 n / 2 로 하고 각 패스마다 간격을 절반으로 줄여 간격이 1이 될 때까지 셸 정렬을 수행하면 된다.

void inc_insertion_sort(int list[], int first, int last, int gap) {
	int i, j, key;
	for (i = first + gap; i <= last; i += gap) {
		key = list[i];
		for (j = i - gap; first <= j && key < list[j]; j -= gap)
			list[j + gap] = list[j];
		list[j + gap] = key;
	}
	return;
}

void shell_sort(int list[], int n) {
	int i, gap;
	for (gap = n / 2; gap > 0; gap /= 2) {
		if (gap % 2 == 0)
			gap++;
		for (i = 0; i < gap; i++)
			inc_insertion_sort(list, i, n - 1, gap);
	}
}

셸 정렬 복잡도

 셸 정렬의 동작 원리는 삽입 정렬을 이용해서 정렬합니다. 따라서, 최악의 경우 , 최선의 경우가 삽입 정렬과 동일한 복잡도를 가집니다. 하지만 실험적인 연구를 통해서 셸 정렬의 평균 시간 복잡도는 삽입 정렬과 다르게 n^(1.5) 를 따른다고 합니다.

 


셸 정렬 안정성

 셸 정렬은 같은 값이 있는 경우 같은 값들의 정렬 이후 순서가 초기 순서와 달라질 수 있는 불안정 정렬에 속합니다.

 [ 5-1 5-2 3 2 1 ] 를 셸 정렬로 정렬해보겠습니다.

[ 5-1 5-2 3 2 1] → [ 1 2 3 5-2 5-1 ]

 위와 같이 셸 정렬은 같은 값이 있는 경우에 같은 값들의 정렬 이후 순서가 초기 순서와 달라질 수 있습니다.


이상으로 셸 정렬 포스팅을 마치겠습니다.

 

안녕하세요, 여행벌입니다.

C언어로 쉽게 풀어쓴 자료구조 9장 - 우선순위 큐 연습문제 풀이입니다.

틀린 부분이나 궁금하신 부분은 편하게 댓글에 남겨주세요!

9장연습문제풀이.zip
1.45MB


[ 11번 ]

element 구조체만 변경해주면 어려움 없이 구현할 수 있습니다.

 

[ 14번, 15번 ]

14번, 15번은 우선순위 큐를 배열과 연결리스트로 구현해보라는 문제입니다.

복잡도가 히프로 구현한 거에 비해 안 좋기 때문에 아마 앞으로 구현할 일이 없지 않을까 싶습니다...

둘 다 최댓값을 반환해주는 우선순위 큐로 구현했습니다.

최솟값을 반환하는 우선순위 큐는 부등호만 바꾸시면 될 것 같습니다.

 

[ 16번 ]

히프 delete 연산을 제대로 이해하셨다면 특정 원소를 찾은 후 똑같이 진행하면 됩니다.

안녕하세요, 여행벌입니다.

C언어로 쉽게 풀어쓴 자료구조 8장 - 트리 연습문제 풀이입니다.

틀린 부분이나 궁금하신 부분은 편하게 댓글에 남겨주세요!

8장연습문제풀이.zip
1.34MB


[ 6번 ]

문제가 잘못된 것 같습니다. 답이 보기에 없습니다.

 

[ 12번 ]

+) 풀이에는 제가 트리의 모든 노드 중 가장 큰 값을 반환한다고 적어놓았는데, 트리의 리프 노드 중 가장 큰 값을 반환하는 함수입니다! 풀이 수정하겠습니다~!

 

[ 13번 ]

재귀적으로 서브트리도 밸런스 트리인지 아닌지 확인하면 됩니다.

 

[ 19번 ]

이진 탐색 트리는 오른쪽 서브트리의 값들이 더 큽니다.

따라서, 가장 오른쪽부터 순회를 하면 내림차순으로 출력할 수 있습니다.

 

[ 22번 ]

책에 나와있는 '사전' 코드와 너무 유사해서 다루지 않았습니다.

 

안녕하세요, 여행벌입니다.

C언어로 쉽게 풀어쓴 자료구조 7장 - 연결리스트2 연습문제 풀이입니다.

틀린 부분이나 궁금하신 부분은 편하게 댓글에 남겨주세요!

7장연습문제풀이.zip
0.00MB

 

+ Recent posts