티스토리 뷰
단순한 정렬 알고리즘
버블 정렬(Bubble Sort) : 인접한 두 개의 데이터를 비교해가면서 정렬을 진행하는 방식
BubbleSort.c
#include <stdio.h>
void BubbleSort(int arr[], int n)
{
int i, j;
int temp;
for(i=0; i<n-1; i++)
{
for(j=0; j<(n-i)-1; j++)
{
if(arr[j] > arr[j+1])
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
int main(void)
{
int arr[4] = {3, 2, 4, 1};
int i;
BubbleSort(arr, sizeof(arr)/sizeof(int));
for(i=0; i<4; i++)
printf("%d ", arr[i]);
printf("\n");
return 0;
}
성능평가
1. 비교의 횟수(빅-오를 결정하는 기준) O(n^2)
2. 이동의 횟수(세밀한 비교) 최선 0 최악- O(n^2)
선택 정렬(Selection Sort) : 정렬순서에 맞게 하나씩 선택해서 옮기는, 옮기면서 정렬이 되게 하는 알고리즘
SelectionSort.c
#include <stdio.h>
void SelSort(int arr[], int n)
{
int i, j;
int maxIdx;
int temp;
for(i=0; i<n-1; i++)
{
maxIdx = i; // 정렬 순서상 가장 앞서는 데이터의 index
for(j=i+1; j<n; j++) // 최소값 탐색
{
if(arr[j] < arr[maxIdx])
maxIdx = j;
}
/* 교환 */
temp = arr[i];
arr[i] = arr[maxIdx];
arr[maxIdx] = temp;
}
}
int main(void)
{
int arr[4] = {3, 4, 2, 1};
int i;
SelSort(arr, sizeof(arr)/sizeof(int));
for(i=0; i<4; i++)
printf("%d ", arr[i]);
printf("\n");
return 0;
}
성능평가
1. 비교의 횟수(빅-오를 결정하는 기준) O(n^2)
2. 이동의 횟수(세밀한 비교) 항상 O(n)
버블정렬과 선택정렬의 우열을 가리는 것은 무의미
삽입 정렬(Insertion Sort) : 앞에 삽입을 해가며 정렬(정렬된 부분과 되지 않은 부분으로 나뉨)
InsertionSort.c
#include <stdio.h>
void InserSort(int arr[], int n)
{
int i, j;
int insData;
for(i=1; i<n; i++)
{
insData = arr[i]; // 정렬 대상을 insData에 저장
for(j=i-1; j>=0 ; j--)
{
if(arr[j] > insData)
arr[j+1] = arr[j]; // 비교 대상 한 칸 뒤로 밀기
else
break; // 삽입 위치 찾았으니 탈출!
}
arr[j+1] = insData; // 찾은 위치에 정렬 대상 삽입!
}
}
int main(void)
{
int arr[5] = {5, 3, 2, 4, 1};
int i;
InserSort(arr, sizeof(arr)/sizeof(int));
for(i=0; i<5; i++)
printf("%d ", arr[i]);
printf("\n");
return 0;
}
성능평가
1. 비교의 횟수(빅-오를 결정하는 기준) O(n^2)
2. 이동의 횟수(세밀한 비교) O(n^2)
복잡하지만 효율적인 정렬 알고리즘
힙 정렬(Heap Sort) : 힙의 루트 노드에 저장된 값이 가장 크게(최대 힙)
HeapSort.c
#include <stdio.h>
#include "UsefulHeap.h"
int PriComp(int n1, int n2)
{
return n2-n1;
// return n1-n2;
}
void HeapSort(int arr[], int n, PriorityComp pc)
{
Heap heap;
int i;
HeapInit(&heap, pc);
// 정렬 대상을 가지고 힙을 구성한다.
for(i=0; i<n; i++)
HInsert(&heap, arr[i]);
// 순서대로 하나씩 꺼내서 정렬을 완성한다.
for(i=0; i<n; i++)
arr[i] = HDelete(&heap);
}
int main(void)
{
int arr[4] = {3, 4, 2, 1};
int i;
HeapSort(arr, sizeof(arr)/sizeof(int), PriComp);
for(i=0; i<4; i++)
printf("%d ", arr[i]);
printf("\n");
return 0;
}
성능평가
힙의 데이터 저장 시간 복잡도 O(log 2 n)
힙의 데이터 삭제 시간 복잡도 O(log2 n)
시간 복잡도 : O(log2 n)
정렬 과정에 대한 시간 복잡도 : O(nlog2 n)
병합 정렬(Merge Sort) : 분할 정복이라는 알고리즘에 근거하여 만들어진 정렬
1단계 분할(divide) : 해결이 용이한 단계까지 문제를 분할해 나간다.
2단계 정복(Conquer) : 해결이 용이한 수준까지 분할된 문제를 해결한다.
3단계 결합(Combine) : 분할해서 해결한 결과를 결합하여 마무리한다.
1개까지 분할하고 2개, 4개씩 병합한다.
MergeSort.c
#include <stdio.h>
#include <stdlib.h>
void MergeTwoArea(int arr[], int left, int mid, int right)
{
int fIdx = left;
int rIdx = mid+1;
int i;
int * sortArr = (int*)malloc(sizeof(int)*(right+1));
int sIdx = left;
while(fIdx<=mid && rIdx<=right)
{
if(arr[fIdx] <= arr[rIdx])
sortArr[sIdx] = arr[fIdx++];
else
sortArr[sIdx] = arr[rIdx++];
sIdx++;
}
if(fIdx > mid)
{
for(i=rIdx; i<=right; i++, sIdx++)
sortArr[sIdx] = arr[i];
}
else
{
for(i=fIdx; i<=mid; i++, sIdx++)
sortArr[sIdx] = arr[i];
}
for(i=left; i<=right; i++)
arr[i] = sortArr[i];
free(sortArr);
}
void MergeSort(int arr[], int left, int right)
{
int mid;
if(left < right)
{
// 중간 지점을 계산한다.
mid = (left+right) / 2;
// 둘로 나눠서 각각을 정렬한다.
MergeSort(arr, left, mid);
MergeSort(arr, mid+1, right);
// 정렬된 두 배열을 병합한다.
MergeTwoArea(arr, left, mid, right);
}
}
int main(void)
{
int arr[7] = {3, 2, 4, 1, 7, 6, 5};
int i;
// 배열 arr의 전체 영역 정렬
MergeSort(arr, 0, sizeof(arr)/sizeof(int)-1);
for(i=0; i<7; i++)
printf("%d ", arr[i]);
printf("\n");
return 0;
}
성능평가
비교의 횟수 : O(nlog2 n)
이동의 횟수 : O(nlog2 n)
퀵 정렬(Quick Sort) : 병합 정렬과 마찬가지로 분할 정복에 근거하여 만들어진 정렬 방법
설명 : 생략... 코드보고
#include <stdio.h>
void Swap(int arr[], int idx1, int idx2)
{
int temp = arr[idx1];
arr[idx1] = arr[idx2];
arr[idx2] = temp;
}
int Partition(int arr[], int left, int right)
{
int pivot = arr[left]; // 피벗의 위치는 가장 왼쪽!
int low = left+1;
int high = right;
while(low <= high) // 교차되지 않을 때까지 반복
{
/*
while(pivot > arr[low])
low++;
while(pivot < arr[high])
high--;
*/
while(pivot >= arr[low] && low <= right)
low++;
while(pivot <= arr[high] && high >= (left+1))
high--;
if(low <= high) // 교차되지 않은 상태라면 Swap 실행
Swap(arr, low, high); // low와 high가 가리키는 대상 교환
}
Swap(arr, left, high); // 피벗과 high가 가리키는 대상 교환
return high; // 옮겨진 피벗의 위치 정보 반환
}
void QuickSort(int arr[], int left, int right)
{
if(left <= right)
{
int pivot = Partition(arr, left, right); // 둘로 나눠서
QuickSort(arr, left, pivot-1); // 왼쪽 영역을 정렬
QuickSort(arr, pivot+1, right); // 오른쪽 영역을 정렬
}
}
int main(void)
{
// int arr[7] = {3, 2, 4, 1, 7, 6, 5};
int arr[3] = {3, 3, 3};
int len = sizeof(arr) / sizeof(int);
int i;
QuickSort(arr, 0, sizeof(arr)/sizeof(int)-1);
for(i=0; i<len; i++)
printf("%d ", arr[i]);
printf("\n");
return 0;
}
시간복잡도 : O(nlog2 n)
최선의 경우의 빅-오. 퀵 정렬은 예외. 중간에 가까운 피벗을 선택하는 방법을 적용.
최악은 O(n^2)
퀵 정렬은 다른 알고리즘보다 평균적으로 제일 빠르다.
'책 > 윤성우의 열혈 자료구조' 카테고리의 다른 글
탐색(Search) 2 (0) | 2020.10.31 |
---|---|
탐색(Search) 1 (0) | 2020.10.30 |
우선순위 큐와 힙 (0) | 2020.10.25 |
트리(Tree) (0) | 2020.10.23 |
큐(Queue) (0) | 2020.10.22 |