1. 버블 정렬 ( Bubble Sort )
- 과정
인접한 두 개의 데이터를 비교해가며 정렬을 진행한다.
가장 크기가 큰 값 ( 또는 정렬의 우선 순위가 가장 낮은 값 ) 을 맨 뒤로 보내고,
그 다음으로 크기가 큰 값을 맨 뒤에서 두번째로 보내고.. 이 과정을 반복한다.
- 시간 복잡도
데이터의 수가 n 개일 때,
제일 처음 비교 횟수 : n-1
그 다음 비교 횟수 : n-2
(n-1) + (n-2) .... + 2 + 1 = n(n-1)/2 = O(n⌒2)
2. 선택 정렬 ( Selection Sort )
- 과정
가장 크기가 작은 값 ( 또는 정렬의 우선 순위가 가장 큰 값 ) 을 선택해서 가장 왼쪽으로 이동시키고,
원래 그 자리에 있던 데이터는 빈 자리에 가져다 놓는다.
- 시간 복잡도
데이터의 수가 n 개 일때, ( 배열이니까 0~ n-1 )
제일 처음 비교 횟수: 1에서 n-1까지 보니까 n-1
그 다음 비교 횟수: 2에서 n-1 까지 보니까 n-2
(n-1) + (n-2) .... + 2 + 1 = n(n-1)/2 = O(n⌒2)
** 버블 정렬과 시간 복잡도는 똑같지만, 데이터의 이동 횟수에는 차이가 있다.
데이터의 교환이 이뤄질 때
temp = A;
A = B;
B = temp;
이므로, 총 3번의 연산이 일어난다.
선택 정렬의 경우 안쪽 for문 바깥쪽에서 이 연산이 일어나고, 버블 정렬의 경우 안쪽 for문의 안쪽에서 이 연산이 일어난다.
따라서 데이터 이동 횟수의 시간 복잡도는, 선택 정렬은 O(n)이고 버블 정렬은 O(n⌒2) 이다.
** 그러나 버블 정렬의 경우 최선의 경우에는 단 한번의 데이터 이동도 발생하지 않으나, 선택 정렬의 경우 최악이나 최선이나 데이터 이동이 같다.
따라서 버블 정렬이나 선택 정렬이나 우열을 가릴 수 없다.
3. 삽입 정렬 ( Insertion Sort )
- 과정
정렬이 완료된 부분과 완료되지 않은 부분으로 나눈 후, 정렬 안된 데이터를 정렬 된 데이터의 특정 위치에 삽입한다.
정렬이 완료된 영역 다음의 데이터가 정렬해야할 데이터이므로,
만약 정렬해야할 데이터의 위치가 k 라면, k-1 위치의 데이터와 비교하여 자신이 더 작으면 k-1의 데이터를 k 위치로 이동시킨다.
다음 k-2의 데이터와 비교하고, 만약 k-2보다 자신이 크다면 k-1의 위치에 정착한다.
= 데이터를 한 칸씩 뒤로 밀면서 내가 들어갈 자리를 찾는다.
- 시간 복잡도
최악의 경우 모든 데이터를 보고 모든 데이터를 비교하며 이동시켜야하므로
1 + 2 + .... + (n-1) = n(n-1)/2 = O(n⌒2)
4. 힙 정렬 ( Insertion Sort )
- 과정
힙의 루트 노드에 저장된 값이 가장 커야 한다는 특성을 힙의 루트 노드에 저장된 값이 정렬 순서상 가장 앞선다는 특징을 갖도록 하여 정렬에 이용한다.
데이터를 모두 힙에 넣은 후, 히벵서 다시 데이터를 꺼낸다.
- 시간 복잡도
힙의 데이터 저장 시간 복잡도와 삭제 시간 복잡도는 O(log n)이다.
n개의 데이터가 있을 때 총 n 개의 데이터를 삽입 및 삭제해야 하므로 O(n log n)이다.
5. 병합 정렬 ( Merge Sort )
- 과정
데이터가 1개만 남을 때 까지 분할을 한다.
다시 데이터를 합칠 때, 우선순위를 고려하여 묶는다.
- 시간 복잡도
데이터의 수가 n 개일때, 각 병합 단계마다 최대 n번의 비교연산이 진행된다.
데이터가 8개일 때, 병합은 3번 진행되고, 16개일때, 병합은 4회 진행된다.
병합 과정의 횟수는 log n 이다.
따라서 시간 복잡도는 O(n log n) 이다.
6. 퀵 정렬 ( Quick Sort )
- 과정
가장 처음 값을 피벗으로 잡는다.
a : 왼쪽에서 오른쪽으로 피벗보다 큰 값을 만날때까지 이동하고,
b: 오른쪽에서 왼쪽으로 피벗보다 작은값을 만날때까지 이동한다.
그리고 그 값을 교환한다. 만약 a와 b가 교차하게 되면 ( a>b)가 되면 탐색을 멈추고, b와 피벗을 교환한다.
-> 이 과정을 통해 피벗의 왼쪽에는 피벗보다 작은 값들만, 오른쪽에는 피벗보다 큰 값들만 있게 된다.
그리고 피벗을 중심으로 왼쪽과 오른쪽을 나누어 재귀적으로 진행한다.
사실 피벗이 가장 중간값이라면 제일좋은 성능을 보일 수 있다. 만약 이미 잘 정렬되있는 배열을 돌린다면, 1부터 n-1까지의 값들이 순서대로 피벗이 되어 정렬을 하게 된다.
중간 값을 찾는 알고리즘이 있긴 하지만 오히려 그거 때문에 성능이 더 나빠져서 실제로는 안쓴다고 한다.
그냥 세 개 데이터를 추출하여 그 중에 중간 값을 피벗으로 선택하는 것이 ....
- 시간 복잡도
하나의 피벗이 자신의 자리를 찾아가기 위해 n번의 비교 연산이 진행된다.
그리고 분할은 총 log n 번 진행된다. ( 피벗이 중간값인 경우 )
따라서 시간 복잡도는 O(n log n)
** 피벗이 중간 값일 때를 최선의 경우라 생각할 수 있지만, 퀵 정렬의 경우 예외적으로 중간에 가까운 피벗을 선택하는 방법을 적용하여 최선에 가까운 성능을 평균적으로 보인다고 한다.
** 병합 정렬과 시간 복잡도는 같지만, 별도의 메모리 공간을 필요로 하지 않으므로 실제로 가장 빠르다.
7. 기수 정렬 ( Radix Sort )
- 소개
기수 정렬은 비교 연산을 하지 않고 데이터를 정렬할 수 있다. 그러나 데이터의 길이가 제한적일 수 있다.
간단하게 생각해보면, 0,8,3,5,4의 데이터를 비교한다면, 크기 10짜리의 배열의 인덱스의 그 값을 넣고 ( arr[8] = 8 이런식 ) 그냥 배열을 순차적으로 출력하면 되는 것...
하지만 이를 좀 바꾸어서, 3자리 수의 데이터 비교를 5개의 버킷(공간)을 가지고 정렬할 수 있다. 이를 LSD 기수 정렬(Least Significant Digit)라고 하며, 덜 중요한 자리 수인 일의자리수 부터 정렬을 진행해 나간다는 것이다.
- 과정
데이터: 134, 224, 232, 122
일의자리수 기준으로 하여 순서대로 버킷에 넣는다.
버킷 2: 122, 232 ( -> 넣은 순서)
버킷 5: 224, 134
현재 상태 : 232, 122, 134, 224 ( 꺼낼때는 먼저 넣은 것부터 )
십의 자리수 기준으로 버킷에 넣는다.
버킷2: 224, 122
버킷3: 134, 232
현재 상태 : 122, 224, 232, 134
백의 자리수 기준으로 버킷에 넣는다.
버킷 1: 134, 122
버킷 2: 232, 224
현재 상태 : 122, 134, 224, 232
** MSD 기수 정렬 (Most Significat Digit)은 가장 중요한 자리수, 큰 자리수부터 정렬을 진행한다. 이 정렬의 가장 큰 장점은 중간에 정렬이 완료될 수 있으므로 끝까지 가지 않아도 된다. 그러나 중간에 정렬이 완료되었는데도 불구하고 끝까지 정렬할 시 정렬이 제대로 되지 않는다. 따라서 중간에 데이터를 점검해야하는 상황이 발생한다. ( 모든 데이터에 일괄적 과정을 거칠 수 없다 ) 따라서 이를 해결할 알고리즘을 필요로 하는데 이게 더 시간이 오래 걸릴 수 있음...
- 시간 복잡도
비교연산이 아닌 데이터의 삽입과 추출이 핵심 연산이다.
가장 긴 데이터의 길이만큼 반복하고, 정렬대상의 수 만큼 버킷에 데이터를 삽입하고 추출한다.
데이터의 길이(l) x 데이터의 수(n) = O(ln) = O(n)
'Tech > Software' 카테고리의 다른 글
스위치 구분(L2, L3, L4, L7) (0) | 2019.09.03 |
---|---|
Google Cloud Essential Workshop (0) | 2019.09.03 |
EJB, JTA, XA, JMS (0) | 2019.03.09 |
java jvm core dump (1) | 2018.12.30 |
HTTP Status Code List (0) | 2018.12.13 |