Last updated
Last updated
어떤 정렬을 쓰냐에 따라 프로그램의 효율성이 좌지우지될 수 있다.
정렬은 면접 단골 질문이기도 하다.
버블 정렬, 선택 정렬, 삽입 정렬, 퀵 정렬, 계수 정렬, 병합 정렬, 힙 정렬 정도까지는 알고 있으면 좋다.
왼쪽부터 자기 오른쪽 요소와 비교해 본인이 더 크면 swap 한 번의 순회마다 큰(오른쪽) 자리의 요소가 정해진다.
작다고 멈추고 이런 게 없기 때문에 최선의 경우, 최악의 경우 모두 동일하게 O(N^2)
순회하며 제일 작은 데이터를 맨 앞으로 옮김으로써 왼쪽부터 정렬을 완성해가는 방식 한 번의 순회마다 작은(왼쪽) 자리의 요소가 정해진다.
다음과 같은 이유로 O(N^2)
이다.
(N-1) + (N-2) + ... + 2 = N(N+1)/2 라서
반복문이 두 번 중첩되었으므로
따라서 알고리즘 문제 풀이에 사용하기에는 조금 느리다.
2번째 데이터부터 시작하여, 자기 왼쪽의 데이터를 확인해 더 크다면 swap, 자신보다 작다면 멈춤으로써 왼쪽부터 정렬을 완성해가는 방식 한 번의 순회마다 왼쪽의 정렬된 데이터 수가 1개씩 늘어난다.
삽입 정렬은 움직일 데이터를 기준으로 내 왼쪽 애들의 순서는 모두 정렬된 상태
라는 전제(실제로 그러하다)로 동작한다.
선택 정렬과 마찬가지로 O(N^2)
이다.
그러나 최선의 시간복잡도는 O(N)
이기 때문에 거의 정렬된 상태의 자료에서 효율적
이다.
기준 데이터(피벗)를 설정하고 기준 데이터를 기점으로 왼쪽에는 보다 작은 데이터가, 오른쪽에는 보다 큰 데이터가 오게 하는 방식
첫 번째 원소를 피벗으로 잡고 왼쪽에서부터 피벗보다 큰 데이터를, 오른쪽에서부터 피벗보다 작은 데이터를 찾는다.
찾은 후 두 데이터가 엇갈리지 않았다면 swap, 엇갈렸다면 작은 데이터와 피벗의 위치를 바꾼다.
2번까지 수행하고나면 피벗을 기준으로 왼쪽은 보다 작은 데이터가, 오른쪽은 보다 큰 데이터가 존재하게 된다.
어쨌든 피벗을 정하고 피벗보다 왼쪽에는 작은 데이터, 오른쪽에는 큰 데이터가 오게 만든 뒤 재귀로 각 배열에 대해 동일한 알고리즘을 적용한다면 퀵소트는 돌아간다.
아래는 이러한 점에서 착안한 파이썬에 특화된 코드이다.
위와 달리 배열 자체를 정렬하는 것이 아니라 정렬한 배열을 반환한다.
최선의 경우
항상 피벗이 정중앙에 위치하게 될 때
즉, 항상 정중앙을 기준으로 배열을 쪼개 재귀에 들어가는 경우
이 때 시간 복잡도는 O(NlogN)
이다.
최악의 경우
배열이 한 쪽으로 치우쳐 쪼개져 데이터의 개수만큼 쪼개지는 경우
이 때 시간 복잡도는 O(N^2)
이다.
따라서 위와 같이 피벗을 선정하는 경우 퀵 정렬은 오히려 정렬된 데이터에서 느리고, 무작위 데이터에서 빨라진다.
이를 보완하기 위해 별도의 피벗 선정 알고리즘을 이용하기도 한다.
모든 데이터를 담을 수 있는 리스트를 선언해 등장 횟수를 세는 방식
지금까지 본 선택, 삽입, 퀵 정렬은 비교 기반의 정렬 알고리즘
이다.
계수 정렬은 데이터를 카운트할 뿐이기 때문에 비교 기반의 정렬 알고리즘이 아니다.
데이터의 개수 N과 최대값의 크기 K에 대하여,
배열 돌면서 각 요소별로 count 증가 O(N)
0부터 최대값 K까지 돌면서 순서대로 새 배열에 삽입 O(K)
두 개를 각각 독립적으로 수행하므로 O(N + K)
0과 999,999 단 두 개의 데이터에 대한 정렬을 수행한다면 매우 비효율적인 알고리즘이 될 것이다.
그러나 데이터의 크기가 작고 중복된 데이터가 많은 경우
에는 효율적인 알고리즘이다.
따라서, 보통 중복 데이터가 있고 이들의 개수를 세야 하는 경우 해당 알고리즘을 사용한다.
우선도
의 의미를 갖는 경우정렬을 통해 우선순위를 기점으로 데이터를 처리해야 하는 문제에 해당한다.
시간 복잡도를 잘 고려해야 한다. 내가 반복문 중첩을 해도 될지 아닐지 등을 잘 생각할 것
정렬 조건 간의 우선순위가 있는 경우 역순으로 적용한다. (즉, 우선도가 낮은 정렬 조건을 먼저 적용)
이유: 먼저 적용하는 조건에서 같은 우선도를 갖는 애들에 다음 조건을 적용하는 것이므로 먼저 적용할 조건을 나중에 적용하면 앞에서 정렬된 (같은 우선도를 가질) 애들의 순서에는 영향을 미치지 않는다.