본문 바로가기

코딩테스트 준비

정렬 알고리즘

1. 선택 정렬

데이터가 무작위로 여러 개 있을 때, 이 중에서 가장 작은 데이터를 선택해 맨 앞에 있는 데이터와 바꾸고, 그다음 작은 데이터를 선택해 앞에서 두 번째 데이터와 바꾸는 과정을 반복. - 가장 원시적인 방법으로 매번 가장 작은 것을 선택한다는 의미에서 선택정렬 알고리즘이라고 한다. 

선택 정렬의 시간 복잡도

선택 정렬은 n-1번 만큼 가장 작은 수를 찾아서 맨 앞으로 보내야 한다. 또한 매번 가장 작은 수를 찾기 위해서 비교 연산이 필요하다. 따라서 약 n x (n + 1) / 2 번의 연산을 수행한다고 할 수 있다.  - O(N**) = 매우 비효율적.

2. 삽입 정렬

특정한 데이터를 적절한 위치에 삽입한다는 의미에서 삽입 정렬이라고 부른다. 더불어 삽입 정렬은 특정한 데이터가 적절한 위치에 들어가기 이전에, 그 앞까지의 데이터는 이미 정렬되어 있다고 가정한다. 정렬되어 있는 데이터 리스트에서 적절한 위치를 찾은 뒤에, 그 위치에 삽입된다는 점이 특징이다. 삽입 정렬은 두 번째 데이터부터 시작한다. 첫 번째 데이터는 그 자체로 정렬되어 있다고 가정하기 때문이다. 

적절한 위치에 삽입하는 과정을 n-1번만 반복하면 됨. 삽입 정렬은 정렬이 이루어진 원소들이 항상 오름차순을 유지하고 있으므로 특정 데이터가 삽입될 위치를 선정할 때 한 칸씩 왼쪽으로 이동하다가 자신보다 작은 데이터를 만나면 그 자리에서 멈추면 된다.

삽입 정렬의 시간 복잡도 역시 O(N**)로 선택 정렬과 마찬가지로 반복문이 2번 중첩되어 사용된다. 그러나 삽입 정렬은 전체를 확인하지 않고 자기가 들어갈 자리를 찾으면 바로 삽입하면 되므로 리스트의 데이터가 거의 정렬되어 있는 상태에서 매우 빠르게 동작한다는 장점이 있다. 최선의 경우 O(N)의 시간 복잡도를 지닌다. 거의 정렬된 상태에서는 퀵 정렬보다도 강력하다. 

3. 퀵 정렬

가장 많이 사용되는 정렬 알고리즘. 병합 정렬 알고리즘 역시 이와 비슷하게 빠른 속도를 낸다. 이 둘은 대부분의 프로그래밍 언어에서 정렬 라이브러리의 근간이 되는 알고리즘이다. 퀵 정렬에서는 기준을 설정한 다음 큰 수와 작은 수를 교환한 후 리스트를 반으로 나누는 식으로 동작한다. 이 때의 기준을 pivot피벗이라고 한다. 퀵 정렬을 수행하기 전에는 피벗을 어떻게 설정할 것인지 미리 명시해야 한다. 피벗을 설정하고 리스트를 분할하는 방법에 따라서 여러 가지 방식으로 퀵 정렬을 구분하는데, 여기서는 가장 대표적인 분할 방식인 Hoare호어 partition분할 방식을 설정한다. 

호어 분할 방식 - 리스트에서 첫 번째 데이터를 피벗으로 정한다. 피벗을 설정한 뒤에는 왼쪽에서부터 피벗보다 큰 데이터를 찾고, 오른쪽에서부터 피벗보다 작은 데이터를 찾는다. 그다음 큰 데이터와 작은 데이터를 서로 교환해준다. 계속 교환하다가 피벗보다 큰 값과 작은 값이 엇갈리게 되면 피벗과 엇갈린 작은 데이터를 교환한다. 이를 분할이라고 한다.

분할 이후 피벗 값 왼쪽에는 모두 피벗보다 작은 값이 위치하고 오른쪽에는 모두 피벗 값보다 큰 값이 온다. 이후 왼쪽 오른쪽에서 각각 또다시 피벗을 설정하여 정렬을 수행하면 된다. 따라서 퀵 정렬은 재귀함수를 사용한다. 퀵 정렬 재귀함수의 종료 조건은 정렬을 수행하는 리스트의 데이터 개수가 1일 때다. *피벗값보다 작은 값이 리스트에 없을 때는 피벗값이 정렬이 된 것이므로 바로 오른쪽에 있는 데이터를 피벗값으로 재설정한다.