380 likes | 588 Views
9 장 . 정렬 (Sorting) 1 절 . 정렬 2 절 . 버블 정렬 3 절 . 선택 정렬 4 절 . 삽입 정렬 5 절 . 쉘 정렬 6 절 . 퀵 정렬 7. 합병 정렬 8. 기수 정렬 9. 히프 정렬. 이 완 직 ( wjlee@pnu.ac.kr ) 2010 년 1 학기. 1. 정렬. 내부 정렬 교환 방식 : 키를 비교하고 교환하여 정렬하는 방식 ( 선택 정렬 , 버블 정렬 , 퀵 정렬 )
E N D
9장. 정렬(Sorting) 1절. 정렬 2절. 버블 정렬 3절. 선택 정렬 4절. 삽입 정렬 5절. 쉘 정렬 6절. 퀵 정렬 7. 합병 정렬 8. 기수 정렬 9. 히프 정렬 이 완 직 (wjlee@pnu.ac.kr) 2010년 1학기
1. 정렬 • 내부 정렬 • 교환 방식: 키를 비교하고 교환하여 정렬하는 방식 (선택 정렬, 버블 정렬, 퀵 정렬) • 삽입 방식: 키를 비교하고 삽입하여 정렬하는 방식 (삽입 정렬, 쉘 정렬) • 병합 방식: 키를 비교하고 병합하여 정렬하는 방식 (2-way 병합, n-way 병합) • 분배 방식: 키를 구성하는 값을 여러 부분집합에 분배하여 정렬 (기수 정렬) • 선택 방식 : 이진 트리를 사용하여 정렬하는 방식 (히프 정렬, 트리 정렬) • 외부 정렬 • 병합 방식: 파일을 부분 파일로 분리하여 각각을 내부 정렬 방법으로 정렬하여 병합하는 정렬 방식(2-way 병합, n-way 병합)
2. 버블 정렬 • 인접한 원소끼리 비교하여 교환 • 오름차순으로 정렬하려면 인접한 요소끼리 비교하여 큰 값을 뒤로 보낸다.
2단계 3단계 4단계 5단계 2. 버블 정렬
6단계 7단계 2. 버블 정렬
3. 선택 정렬(selection sorting) • 가장 작은 카드를 뽑아서 제일 앞에 놓고 그 다음 작은 카드를 뽑아서 두 번째 위치에 놓는 식의 작업을 반복하여 정렬한다. • 방법(배열 크기 7, 오름차순 정렬 가정) • 기준 위치값인 base는 0부터 5까지 변경된다. • 비교 대상은 기준위치보다 하나 큰 값에서 배열의 마지막 요소의 위치인 6까지 변경된다. • 기준위치의 값과 비교대상의 대소 관계를 물어보고 비교 대상의 값이 작으면 해당 위치의 인덱스 값을 변수(min)에 저장해둔다.
4. 삽입 정렬 • 이미 정렬이 된 부분에 새로운 키를 적절한 장소에 삽입하는 동작을 반복적으로 하는 정렬 방법 • 절차 • 초기 상태 • 첫 번째 원소는 정렬되어 있는 부분 집합 S로 생각하고 나머지 원소들은 정렬되지 않은 원소들의 부분 집합 U로 생각
4. 삽입 정렬 • U의 첫 번째 원소 Y를 S의 마지막 원소 G와 비교하면 (G < Y)이고 더 이상 비교할 S의 원소가 없으므로 제자리에 둔다. • U의 첫 번째 원소 B를 S의 마지막 원소 Y와 비교하면 (B < Y)이므로 원소 Y의 앞자리 원소 G와 비교한다. (B < G)이므로 원소 B는 원소 G의 앞자리가 된다.
4. 삽입 정렬 • U의 첫 번째 원소 R을 S의 마지막 원소 Y와 비교하면 (R < Y)이므로 원소 Y의 앞자리 원소 G와 비교하고 (R > G)이므로 G와 Y사이에 R을 삽입한다. • U의 첫 번째 원소 P를 S의 마지막 원소 Y와 비교하면 (P < Y)이므로 그 앞자리 원소 R과 비교한다. (P < R)이므로 다시 그 앞자리 원소 G와 비교한다. (P > G)이므로 원소 G와 R 사이에 삽입한다.
4. 삽입 정렬 • U의 첫 번째 원소 C를 S의 마지막 원소 Y와 비교하면 (C < Y)이므로 그 앞자리 원소 R과 비교한다. (C < R)이므로 그 앞자리 원소 P와 비교한다. (C < P)이므로 다시 그 앞자리 원소 G와 비교하는데, (C < G)이므로 그 앞자리 원소 B와 비교하여 (C > B)이므로 원소 B와 G사이에 삽입한다.
4. 삽입 정렬 • U의 첫 번째 원소W를 S의 마지막 원소 Y와 비교하면 (W < Y)이므로 제자리에 둔다.
5. 쉘 정렬 • 삽입 정렬이 어느 정도 정렬된 배열에 대해서는 대단히 빠른 것에 착안한 방법 -> 쉘 정렬은 삽입 정렬보다 빠르다 • 전체 리스트를 일정 간격의 부분 리스트로 나누고 부분 리스트를 정렬하기 때문에 요소들이 멀리 떨어진 위치로도 이동할 수 있다. • 일정한 간격(interval)으로 떨어져 있는 자료들끼리 부분집합을 구성 • 각 부분집합에 있는 원소들에 대해서 삽입 정렬을 수행하는 작업을 반복하면서 전체 원소들을 정렬
5. 쉘 정렬 • 원소의 개수가 8개이므로 매개변수 h는 4에서 시작한다. 간격이 4인 원소들을 같은 부분 집합으로 만들면 4개의 부분 집합이 만들어진다. • 각 부분 집합에 대한 삽입 정렬을 수행한다. {69, 16} {10, 8} {30, 31} {2, 22}
5. 쉘 정렬 • 이제 h를 2로 변경하고 다시 쉘 정렬을 수행한다. 간격이 2인 원소들을 같은 부분 집합으로 만들면 2개의 부분 집합이 만들어진다. • 각 부분 집합에 대한 삽입 정렬을 수행한다. {16, 30, 69, 31} {8, 2, 10, 22}
5. 쉘 정렬 • 이제 h를 1로 변경하고 다시 쉘 정렬 시작한다. 간격이 1인 원소들을 같은 부분 집합으로 만들면 1개의 부분 집합이 만들어진다 • 성능 분석 • h의 영향을 받기 때문에 성능 분석이 쉽지 않음 • 일반적인 복잡도는 O(n1.25) • 이전의 정렬 방법의 성능 O(n2)보다 개선된 정렬 방법
6. 퀵 정렬 • 버블 정렬이나 선택 정렬에 비하여 빠른 속도로 정렬을 수행 • 연속적인 분할을 하면서 정렬을 수행 • 축(Pivot)값을 중심으로 왼쪽은 이 축 값보다 작은 값으로, 오른쪽은 이 축 값보다 큰 값으로 재배열시킴 • 축을 기준으로 왼쪽과 오른쪽을 분할한 뒤 각 부분에 따로 축을 두고 축을 기준으로 왼쪽, 오른쪽에 축 값을 재배열한 후 또다시 분할함 • 이 과정을 분할의 크기가 1이 될 때까지 반복하면 전체적으로 정렬이 완료됨
6. 퀵 정렬 • Pivot: 6, left:0, right:5 • left를 증가시키면서 Pivot 값(37)보다 큰 값을 찾음 • right를 감소시키면서 Pivot 값(37)보다 작은 값을 찾음 • 값 45(left:0), 값 5(right:5): 교환 • 값 87(left:1), 값 35(right:3): 교환
6. 퀵 정렬 • 값 87(left:3), 10(right:2) => left가 right를 넘어감: 분할 완료 인식 • 분할 완료 시: 값 87(left:3)과 pivot(6)의 값(87)을 교환 • 이제 배열은 기존 축 값(37)을 기준으로 37보다 작은 왼쪽 영역과 37보다 큰 오른쪽 영역으로 분할됨. • 각 영역별 동일한 정렬 방법을 적용 • [left 영역] • Pivot:2, left:0, right:1 -> left:1, right:0 : left 값과 pivot 값 교환 • pivot:1(값 10)을 중심으로 두 영역이 또 나누어지나, 구간 크기가 1이므로 종료
6. 퀵 정렬 • [right 영역] • pivot:6(값 87), left:4, right:5 • left:6(값 87), right:5(값 45) => left가 right를 넘어감 • left 값 87과 pivot 값 87을 교환(동일) • 87 값을 기준으로 왼쪽 영역(개수 2개)와 오른쪽 영역(개수 0개)로 분할 • 오른쪽 영역의 개수가 0이므로 종료
6. 퀵 정렬 • [left 영역] • pivot:5, left:4, right:4 • left:4, right:4 => left가 right보다 크거나 같으면 분할 종료이므로 left 값 51과 pivot 값 45를 교환 • 45를 기준으로 영역 분할 • 왼쪽 영역 개수가 0, 오른쪽 영역 개수가 1이므로 정렬 종료
6. 퀵 정렬 • 성능 분석 • pivot 값 선택에 따라 성능이 달라짐 • pivot 값으로 [left 영역], [right 영역]으로 정확히 n/2 씩 이등분되면 성능이 최대 • n개의 레코드에서 하나의 레코드를 위치시키는 데 소요되는 시간은 O(n) • 퀵정렬 시간을 T(n)으로 둘때, T(n) <= cn + 2T(n/2) <= cn + 2(cn/2 + 2T(n/4)) <= 2cn + 4T(n/4) … … <= cnlog2n + nT(1) = O(nlog2n) • 평균 정렬시간: O(nlog2n) • 최악의 정렬시간: O(n2)
7. 합병 정렬(merge sort) • 리스트를 두 개로 나누어 각각을 정렬한 다음 다시 하나로 합치는 방법으로, 분할 정복(divide and conquer)기법에 바탕 • 2-원 합병 정렬 • 분할(divide) : 입력 자료를 같은 크기의 부분 집합 2개로 분할 • 정복(conquer) : 부분집합의 원소들을 정렬한다. 부분집합의 크기가 충분히 작지 않으면, 순환호출을 이용하여 다시 분할 정복 기법을 적용한다. • 결합(combine) : 정렬된 부분집합들을 하나의 집합으로 통합한다.
7. 합병 정렬 • 분할 단계 • 합병 단계
7. 합병 정렬 • 성능 분석 • 합병 정렬은 길이 n인 리스트를 균등 배분:logn개의 패스 • 각 패스 당 n개를 비교 합병: n개의 연산 • O(nlogn) • 배열을 사용할 경우, 각 패스에서 2n번의 레코드 이동 • 연결 리스트를 이용한 합병 정렬은 매우 우수한 정렬 기법
8. 기수 정렬(radix sort) • 버킷 정렬(bucket sort)이라고도 불리며, 분배 방식으로 정렬을 수행한다. • 정렬할 원소의 키 값에 해당하는 버킷에 원소를 분배하였다가, 버킷의 순서대로 원소를 꺼내는 방법을 반복한다.
8. 기수 정렬 • 키값의 일의 자리에 대해서 기수 정렬을 수행한다. • 정렬할 원소의 일의 자리값에 따라서 순서대로 버킷에 분배한다. • 버킷에 분배된 원소들을 순서대로 꺼내어 저장한다.
8. 기수 정렬 • 키 값의 십의 자리에 대해서 기수 정렬을 수행한다. • 정렬할 원소의 십의 자리값에 따라 순서대로 버킷에 분배한다. • 버킷에 분배된 원소들을 순서대로 꺼내어 저장한다.
8. 기수 정렬 • 성능 분석 • O(n)의 성능 • 지금까지의 정렬 기법들의 성능인 O(nlogn)의 한계를 깰 수 있는 유일한 정렬 방법 • 기수 정렬의 단점 • 정렬될 레코드들의 키가 동일한 길이를 가지는 숫자나 문자열일 경우에만 가능한 정렬 기법
9. 히프 정렬 • 히프에서는 항상 가장 큰 원소가 루트 노드가 되고 삭제 연산을 수행하면 항상 루트 노드의 원소를 삭제하여 반환된다. • 내림차순으로 정렬하려면 최대 히프에 대해서 원소의 개수만큼 삭제 연산을 수행하고 • 오름차순으로 정렬하려면 최소 히프에 대해서 원소의 개수만큼 삭제 연산을 수행한다. • 수행 방법 ❶ 정렬할 원소들을 입력하여 최대 히프 구성한다. ❷ 히프에 대해서 삭제 연산(히프 트리의 루트 노드를 출력)을 수행하여 얻은 원소를 마지막 자리에 배치한다. ❸ 나머지 노드들로 새로운 히프을 만드는 과정을 반복한다. ❹ ❷, ❸의 과정을 모든 레코드가 제거될 때까지 반복한다.
9. 히프 정렬 • 성능 분석 • 히프에 삽입하거나 삭제할 때, O(logn) 소요 • 요소의 개수가 n개 이므로 전체 O(nlogn)의 시간이 소요 • 히프 정렬은 자료의 전체 정렬 시보다, 가장 큰(또는 작은) 값 몇 개만 필요할 경우 매우 유용한 기법