[자료구조/C++] 정렬 알고리즘(정의, 시간 복잡도, 구현까지)
Selection Sort
- 정의 : 정렬된 부분과 그렇지 않은 부분으로 나누고, unsorted 영역에서 가장 작은 elements를 sorted 영역으로 올림
- 시간 복잡도 계산
- Sum = (N-1) + (N-2) + (N-1) … + 2 + 1
- Sum = N * (N-1)
- Sum = O(N^2)
-
구현
template<class ItemType> int MinIndex(ItemType values[], int start, int end){ int indexOfMin= start; for(int index = start + 1; index <= end; i++) if(values[index] < values[indexOfMin]) indexOfMin = index; return indexOfMin; } template<class ItemType> void SelectionSort(ItemType values[], int numValues){ int endIndex = numValues - 1; //numValues: value의 개수 for(int current = 0; current <endIndex; current++) Swap(values[current], values[MinIndex(values, current, endInedx)]); }
Bubble Sort
- 정의: 끝에서 시작하여 이웃한 두 인덱스의 위치를 바꿈. 가장 작은 요소가 맨 위로 “bubble up” 됨
- 시간 복잡도: 항상 O(N^2)
- 구현
template<class ItemType>
void BubbleUp(ItemType values[], int start, end){ //인접한
for(int index = end; index > start; index--){
if(values[index] < values[index-1])
Swap(values[index], values[index-1]);
}
}
template<class ItemType>
void BubbleSort(ItemType values[], int numValues){
int current = 0;
while(curruent < numValues - 1){
BubbleUp(values, current, numValues - 1)
current++; //가장 작은 값이 BubbleUp을 하며 맨 위로 올라가므로, current는 항상 정렬된 인덱스이다.
//따라서 하나씩 더해 정렬되지 않은 부분부터 정렬하도록 하게 한다.
}
}
Insertion Sort
- 정의: 새로운 카드를 삽입하는 것 같은 알고리즘. count를 하나씩 늘려나가면서 그 count만큼의 인덱스에서 계속 정렬하는 걸 반복하는 거다. 새로운 카드가 들어오면 또 전체 카드 정렬하고, 또 추가되면 또 전체 정렬하는 알고리즘.
(그림이 좀 구리지만.. 대강 이런 식. 앞에는 이미 정렬된 상태가 된다.)
- 시간복잡도: O(N^2)
- 구현
template<class ItemType>
void InsertItem(ItemType values[], int start, int end){
bool finished = false;
int current = end;
bool moreToSearch = (current != start);
while(moreToSearch && !finished){
if(values[current] < values[current - 1]){
Swap(values[current], values[current-1]);
current--;
moreToSearch = (current != start);
}
else
finished = true;
}
template<class ItemType>
void InsertionSort(ItemType values[], int numValues){
for (int count = 0; count < numValues; count++)
InsertItem(values, 0, count);
}
}
Heap Sort
- 정의: 힙의 성질을 이용한 정렬. root 노드는 항상 가장 큰 값이므로, 리스트의 맨 뒤에 있는 값과 루트 노드를 계속 바꾸어 정렬하는 방법. 리스트의 하단은 이미 완벽히 정렬되어 있으므로, 아래를 고려할 필요가 없어진다.
- 시간 복잡도: Reheap Down 과정에서 각 요소는 자신의 두 자식과 비교되고(더 큰 값과 교환). 하지만 각 레벨에서는 단 한 요소만이 이 비교를 수행하며, N개의 노드를 가진 완전 이진 트리는 O(log2N)개의 레벨만 가진다.
- (N/2) * O(log N) : origin heap 생성
- (N-1) * O(log N) : sorting
- total : O(N * log N)
- 구현
template<class ItemType>
void HeapSort(ItemType values[], int numValues){
int index;
//중간부터 시작해서 ReheapDown을 수행해 큰 값을 위로 올린다
//중간부터 시작하는 이유는, 중간 인덱스보다 큰 인덱스에는 자식 노드가 없는
//leaf 노드이고, 이들은 이미 heap 조건을 만족하기 때문이다.
for(index = numValues /2 - 1; index >=0; i--)
ReheapDown(values, index, numValues - 1);
for(index = numValues - 1; index >= 1; index--){ //아래 코드에 index - 1이 있으므로
Swap(values[0], values[index]);
ReheapDown(values, 0, index - 1); //이미 정렬된 하단 부분을 하나씩 제외
}
}
template<class ItemType>
void ReheapDown(ItemType values[], int root, int bottom){
int maxChild;
int rightChild;
int leftChild;
leftChild = root * 2 + 1;
rightChild = root * 2 + 2;
if(leftChild <= bottom){
if(leftChild == bottom)
maxChild = leftChild;
else{
if(values[leftChild] <= values[rightChild])
maxChild = rightChild;
else
maxChild = leftChild;
if(values[root] < values[maxChild]){
Swap(values[root], values[maxChild]);
ReheapDown(values, maxChild, bottom);
}
}
}
}
Leave a comment