Sorting Algorithm
Bubble Sort
서로 인접한 두 원소를 검사하여 순서에 맞지 않은 요소를 인접한 요소와 교환하며 정렬하는 알고리즘입니다.
선택 정렬 과 기본 개념이 유사합니다.
장점
- 구현이 매우 간단합니다.
단점
- 모든 케이스에 대해서 O(N^2) 입니다.
- 하나의 요소가 가장 왼쪽에서 가장 오른쪽으로 이동하는 경우 배열의 모든 다른 요소들과 교환되어야 합니다.
- 특히 특정 요소가 최종 정렬 위치에 이미 있는 경우라도 교환되는 일이 일어납니다.
Pseudo code
- i = N-1
- if i == 0, stop
- j=0
- if j == i, go to 3.
- if array[j] > array[j+1], swap two values
- j += 1 and go to the 2-2
- i -= 1 and go to the 2.
python code
def bubble_sort(array): for i in range(len(array)-1, 0, -1): # 아래의 loop가 끝났을 때, index가 i 인 원소는 최종위치입니다. for j in range(i): if array[j] > array[j+1]: array[j], array[j+1] = array[j+1], array[j] return array print(bubble_sort([4,3,1,2,6,10,9]))
Selection Sort
제자리 정렬 알고리즘 중 하나입니다.
- 입력 배열(정렬되지 않은 값들) 이외에 다른 추가 메모리를 요구하지 않는 정렬 방법입니다.
해당 순서에 원소를 넣을 위치는 이미 정해져 있고, 어떤 원소를 넣을지 선택하는 알고리즘입니다.
장점
- 자료 이동 횟수가 미리 결정됩니다.
단점
- 모든 케이스에 대해서 O(N^2) 입니다.
- 값이 같은 레코드가 있는 경우에 상대적인 위치가 변경될 수 있기 때문에 안정성을 만족하지 않습니다.
Pseudo code
- i=0
- if i == N-2, stop
- Find the minimum from i to N-1 (store it to the min)
- Swap val[i], min
- i+=1 and go to 2.
python code
def selection_sort(array): for i in range(len(array)-1): min_idx = i for j in range(i+1, len(array)): if array[min_idx] > array[j]: min_idx = j if i != min_idx: array[i], array[min_idx] = array[min_idx], array[i] return array print(selection_sort([4,3,1,2,6,10,9]))
Instert Sort
배열의 모든 요소를 앞에서부터 차례대로 이미 정렬된 배열 부분과 비교하며, 위치를 찾아 삽입함으로써 정렬을 완성하는 알고리즘입니다.
손안의 카드를 정렬하는 방법과 유사합니다.
- 새로운 카드를 기존의 정렬된 카드 사이의 올바른 자리를 찾아 삽입합니다.
- 새로 삽입될 카드의 수만큼 반복하게 되면 전체 카드가 정렬됩니다.
매 순서마다 해당 원소를 삽입할 수 있는 위치를 찾아 해당 위치에 넣습니다.
장점
- 안정적인 정렬 방법입니다.
- 레코드의 수가 적을 경우 알고리즘 자체가 매우 간단하므로 다른 복잡한 정렬 방법보다 유리할 수 있습니다.
- 대부분의 레코드가 이미 정렬되어 있는 경우에 매우 효율적일 수 있습니다.
단점
- 비교적 많은 레코드의 이동을 포함합니다. (최악의 경우 시간복잡도가 비효율적입니다.)
- 레코드 수가 많고 레코드 크기가 클 경우에 적합하지 않습니다.
Pseudo code
- i = 1
- if i == N, stop
- key = a[i], j = i-1
- if j == -1, go to 4.
- if a[j] > key, swap a[j], a[j+1]
- j -= 1 and go to 3-1.
- i+=1 and go to 2.
python code
def insertion_sort(array): for i in range(1,len(array)): key = array[i] for j in range(i-1, -1, -1): if array[j] > key: array[j], array[j+1] = array[j+1], array[j] return array print(insertion_sort([4,3,1,2,6,10,9]))
Merge Sort
일반적인 방법으로 구현했을 때 이 정렬은 안정 정렬에 속하며, 분할 정복 알고리즘의 하나입니다.
- 분할 정복 방법
- 문제를 작은 2개의 문제로 분리하고 각각을 해결한 다음, 결과를 모아서 원래의 문제를 해결하는 전략입니다.
- 분할 정복 방법은 대개 재귀 호출을 이용하여 구현합니다.
- 분할 정복 방법
장점
- 안정적인 정렬 방법입니다.
- 데이터의 분포에 영향을 덜 받습니다. 즉, 입력 데이터가 무엇이든 간에 정렬되는 시간은 동일하다. O(NlogN)
- 만약 레코드를 연결리스트로 구성하면, 링크 인덱스만 변경되므로 데이터의 이동은 무시할 수 있을 정도로 작아집니다.
- 제자리 정렬로 구현할 수 있습니다.
- 따라서 크기가 큰 레코드를 정렬할 경우에 연결 리스트를 사용한다면, 합병 정렬은 퀵 정렬을 포함한 다른 어떤 정렬 방법보다 효율적입니다.
- 안정적인 정렬 방법입니다.
단점
- 만약 레코드를 배열로 구성하면, 임시 배열이 필요합니다.
- 제자리 정렬이 아닙니다.
- 레코드들의 크기가 큰 경우에는 이동 횟수가 많으므로 매우 큰 시간적 낭비를 초래합니다.
- 만약 레코드를 배열로 구성하면, 임시 배열이 필요합니다.
Pseudo code
- 리스트의 길이가 0 또는 1이면 이미 정렬된 것으로 본다. 그렇지 않은 경우에는
- 정렬되지 않은 리스트를 절반으로 잘라 비슷한 크기의 두 부분 리스트로 나눈다.
- 각 부분 리스트를 재귀적으로 합병 정렬을 이용해 정렬한다.
- 두 부분 리스트를 다시 하나의 정렬된 리스트로 합병한다.
python code
def merge(left_arr, right_arr): result = [] left_idx = 0 right_idx = 0 while left_idx < len(left_arr) or right_idx < len(right_arr): if left_idx >= len(left_arr): result.extend(right_arr) break if right_idx >= len(right_arr): result.extend(left_arr) break if left_arr[left_idx] <= right_arr[right_idx]: result.append(left_arr[left_idx]) left_idx += 1 else: result.append(right_arr[right_idx]) right_idx += 1 return result def merge_sort(arr): if len(arr) == 1: return arr mid = len(arr)//2 left_arr = arr[:mid] right_arr = arr[mid:] left_arr = merge_sort(left_arr) right_arr = merge_sort(right_arr) return merge(left_arr,right_arr)
Quick Sort
퀵 정렬은 불안정 정렬에 속하며, 다른 원소와의 비교만으로 정렬을 수행하는 비교 정렬에 속합니다.
분할 정복 알고리즘의 하나로, 평균적으로 매우 빠른 수행 속도를 자랑하는 정렬 방법입니다.
- 합병 정렬과 달리 퀵 정렬은 리스트를 비균등하게 분할합니다.
장점
- 속도가 빠릅니다.
- 시간 복잡도가 O(NlogN)를 가지는 다른 정렬 알고리즘과 비교했을 때도 가장 빠릅니다.
- 추가 메모리 공간을 필요로 하지 않습니다.
- 속도가 빠릅니다.
단점
- 정렬된 리스트에 대해서는 퀵 정렬의 불균형 분할에 의해 오히려 수행시간이 더 많이 걸립니다.
Pseudo code
- 리스트 안에 있는 한 요소를 선택한다. 이렇게 고른 원소를 피벗(pivot)이라고 한다.
- 피벗을 기준으로 피벗보다 작은 요소들은 모두 피벗의 왼쪽으로 옮겨지고 피벗보다 큰 요소들은 모두 피벗의 오른쪽으로 옮겨진다.
- 피벗을 제외한 왼쪽 리스트와 오른쪽 리스트를 다시 정렬한다.
- 분할된 부분 리스트에 대하여 순환 호출을 이용하여 정렬을 반복한다.
- 부분 리스트에서도 다시 피벗을 정하고 피벗을 기준으로 2개의 부분 리스트로 나누는 과정을 반복
- 부분 리스트들이 더 이상 분할이 불가능할 때까지 반복한다.
- 리스트의 크기가 0이나 1이 될 때까지 반복
python code
def quick_sort(arr, left, right): l = left r = right pivot = arr[(left+right)//2] while l<=r : print(l,r) while arr[l] < pivot: l += 1 while arr[r] > pivot: r -= 1 if l <= r: arr[l], arr[r] = arr[r], arr[l] l += 1 r -= 1 if l < right: quick_sort(arr,l,right) if r > left: quick_sort(arr, left, r) return arr
'SW > Algorithm & Data Structure' 카테고리의 다른 글
AVL Tree (0) | 2021.08.01 |
---|---|
Binary Search Tree (0) | 2021.06.13 |
Tree (0) | 2021.05.10 |
Comment