티스토리 뷰

책/윤성우의 열혈 자료구조

정렬(sorting)

안양사람 2020. 10. 28. 01:15
728x90
SMALL

단순한 정렬 알고리즘

버블 정렬(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)

퀵 정렬은 다른 알고리즘보다 평균적으로 제일 빠르다.

728x90
LIST

' > 윤성우의 열혈 자료구조' 카테고리의 다른 글

탐색(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
댓글
공지사항