정렬 알고리즘은 다양한 알고리즘의 기초들을 이용해야 하기 때문에, 이런 부분들을 갖추고 있는지 묻는 면접에서 질문으로 많이 나오게 된다.
이런 알고리즘의 기초에는
점근 표기법
분할 정복 알고리즘 (Divide & Conquer)
최악의 경우, 최선의 경우, 평균적인 경우
같은 개념들이 있으며, 정렬을 설명하기 위해서는 이런 개념들이 필요하다.
점근 표기법은 시간복잡와 공간복잡도를 표현하기 위해서 알고 있어야 하는 개념이고,
분할 정복 알고리즘은 시간복잡도를 줄인 정렬(O(NlogN)) 을 설명하기 위해서 반드시 알고 있어야 하는 개념이다.
정렬에는 여러가지 경우에 따라 시간복잡도가 달라지는 데, 이런 경우를 따지면서 시간복잡도를 계산해내는 방법을 익히기 좋다.
정렬 알고리즘의 출력은 비 내림차순이다. 즉, 이전 원소는 다음 원소보다 작지 않다.
정렬 알고리즘의 출력은 입력을 재배열하여 만든 순열이다.
정렬을 수행하는 데에 추가 메모리가 O(logN) 이하로 사용되는 알고리즘
동일 값의 정렬 전후에 순서가 유지되는 알고리즘
O(n^2) 시간복잡도를 갖는 정렬 방법에는 대표적으로 bubble sort, insertion sort, selection sort가 있다.
해당 정렬방법은, 모든 원소에 대해 모든 원소를 한번씩 비교해줌으로써 O(n^2) 의 시간복잡도를 가지게 되고, 보편적으로 생각해낼 수 있는 정렬 방법이다.
버블 정렬은 특정 원소의 바로 뒤의 값을 모든 원소에 대해서 비교하는 과정을 모든 원소에 대해서 실행한다.
때문에 현재 배열 안에서 추가적인 메모리를 사용하지 않고 공간복잡도 O(1)로 수행이 가능하고, 시간복잡도는
def bubble_sort(x):
# 인접한 두 원소를 검사하여 정렬하고, 마지막 원소를 제외하고
length = len(x) - 1
for i in range(length):
swapped = False
for j in range(length - i):
if x[j] > x[j+1]: # 인접 원소 비교
swapped = True # 스왑을 실행했는지 확인
x[j], x[j+1] = x[j+1], x[j] # 스왑
if not swapped: # 스왑이 없었다면, 정렬이 끝난 것
break
return x
앞 부분의 이미 정렬된 영역과, 아직 정렬되지 않은 영역으로 나눈다.
정렬되지 않은 영역의 값을 정렬된 영역의 적절한 위치에 하나씩 삽입한다.
앞 배열은 이미 정렬된 부분. 하지만 중간중간에 값이 들어갈 수 있다. 즉 정렬되지 않은 값들을 찾아내 정렬된 부분에 하나씩 쑤셔넣는 것. 그래서 삽입 정렬이다.
시간복잡도는 버블 정렬과 같다.
def insertion_sort(x):
for i in range(1, len(x)): # 모든 원소에 대해서
j = i - 1 # i 는 정렬되지않은 배열의 첫 원소, j 는 정렬된 배열의 마지막 원소
key = x[i] # 정렬되지 않은 배열의 원소를 저장해서 정렬된 배열에 넣기 위한 key값
while x[j] > key and j >= 0: # x[j] 가 key 보다 작은 순간일 때 까지.
# 즉 정렬되지 않은 대상 원소가 처음으로 정렬된 원소의 특정 원소보다 작아지는 순간, 정렬이 될 예정
x[j + 1] = x[j] # 값을 삽입하기 위한 배열 인덱스 이동
j -= 1
x[j + 1] = key # 삽입
return x
주어진 리스트 중 최소 값을 찾아, 맨 앞의 값과 교체한다.
맨 처음 위치를 제외하고 위 동작을 반복한다.
최소 값을 찾는데 O(n) 이 소모되고, 모든 원소를 반복하는데 O(n) 이 소모되서 총 O(n^2) 이 소모된다.
시간 복잡도
def selection_sort(x):
length = len(x)
for i in range(length-1): #
index_min = i
for j in range(i+1, length):
if x[index_min] > x[j]:
index_min = j
x[i], x[index_min] = x[index_min], x[i]
return x
모든 O(n^2) 의 시간복잡도를 갖는 정렬 알고리즘은 공간복잡도가 O(1) 이다. 분할정복을 이용해 시간복잡도를 줄이는 정렬 알고리즘의 경우 공간복잡도가 더 크다. 때문에 이 공간복잡도를 희생해 시간복잡도를 줄이는 기법을 통한 정렬 알고리즘도 존재한다.