알고리즘

정렬 알고리즘

Airhood 2024. 10. 15. 18:21

정렬 알고리즘이란?

정렬 알고리즘이란 목록 안에 있는 원소들을 순서대로 만들기 위해 사용하는 알고리즘이다.

 

정렬 알고리즘의 종류

- 선택 정렬

- 버블 정렬

- 삽입 정렬

- 병합(합병) 정렬

- 퀵 정렬

- 힙 정렬

- 기수 정렬

- 셸 정렬

- 카운팅 정렬

등 다양한 종류의 정렬 알고리즘이 있다.

 

그리고 정렬 알고리즘이라 보아야 되는지 모르겠는

보고 정렬(Bogo sort)

 

이 중에서 많이 알려진 선택 정렬, 버블 정렬, 삽입 정렬, 병합 정렬, 퀵 정렬, 힙 정렬에 대해 알아보겠다.

 

정렬 알고리즘을 선택 시 고려해야 할 사항

- 시간 복잡도

- 메모리 사용량

- 안정성

- 데이터의 특성

- 병렬 처리 가능성

안정성?

정렬 알고리즘을 사용했을 때, 같은 값을 가진 두 원소의 순서가 유지된다는 보장이 없다.

안정적인 알고리즘 버블 정렬, 삽입 정렬, 병합 정렬, 기수 정렬, 카운팅 정렬
안정적이지 않은 알고리즘 선택 정렬, 퀵 정렬, 힙 정렬, 셸 정렬

 

 

1. 선택 정렬

선택 정렬은 정렬되지 않은 데이터에서 가장 작은 원소를 찾아서 앞의 데이터와 교환하는 방식이다.

 

과정

  1. 데이터에서 최소값을 찾는다
  2. 그 값을 맨 앞의 값과 교환한다.
  3. 맨 처음 위치를 제외한 나머지 원소들을 대상으로 다시 과정1을 진행한다.

구현 (C++)

void SelectionSort(int* arr, int n) {
	for (int i = 0; i < n - 1; i++) {
		int min = i;
		for (int j = i + 1; j < n; j++) {
			if (arr[j] < arr[min]) {
				min = j;
			}
		}

		int temp = arr[i];
		arr[i] = arr[min];
		arr[min] = temp;
	}
}

 

 

2. 버블 정렬

버블 정렬은 서로 인접한 두 원소를 비교하여 위치를 바꾸는 방식이다.

 

과정

  1. n이 1일때부터 n번재와 n+1번째를 비교하여 n번째가 더 큰 경우에 두 원소를 서로 교환한다.
  2. 1번 순환한 후에 가장 오른쪽 원소는 가장 큰 원소이므로 이를 제외하고 다시 과정1을 진행한다.

 

구현 (C++)

void BubbleSort(int* arr, int n) {
	int temp;
	for (int i = 0; i < n - 1; i++) {
		for (int 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;
			}
		}
	}
}

 

 

3. 삽입 정렬

삽입 정렬은 모든 원소를 앞에서부터 이미 정렬된 부분과 비교하여 정렬되지 않은 원소가 들어갈 위치를 찾아 삽입하여 정렬된 부분을 늘려나가며 정렬을 완성하는 방식이다.

 

과정

  1. 두 번째 원소부터 시작해서 그 앞에 있는 정렬된 부분과 비교하여 삽입할 위치를 찾아 삽입한다.
  2. 과정 1을 데이터의 끝까지 반복한다.

구현 (C++)

void InsertionSort(int* arr, int n) {
	int key;
	for (int i = 1; i < n; i++) {
		key = arr[i];

		int j;
		for (j = i - 1; (j >= 0) && (arr[j] > key); j--) {
			arr[j + 1] = arr[j];
		}
		arr[j + 1] = key;
	}
}

 

 

4. 병합 정렬

데이터를 두 개로 분할하고 분할한 두 개의 데이터를 정렬한 다음, 정렬된 두 개의 데이터를 하나로 병합하여 정렬을 완성하는 방식이다.

 

과정

  1. 데이터를 두 개로 분할한다.
  2. 분할한 데이터를 정렬한다. 분할된 데이터의 크기가 작지 않다면(2개가 아니라면) 재귀 호출을 통해 데이터를 다시 나눈다.
  3. 정렬된 분할한 데이터를 다시 하나로 병합한다.

구현 (C++)

void Merge(int* arr, int left, int mid, int right) {
	int* temp = new int[right - left + 1];
	int i = left;
	int j = mid + 1;
	int k = 0;

	while ((i <= mid) && (j <= right)) {
		if (arr[i] <= arr[j]) {
			temp[k] = arr[i];
			k++;
			i++;
		}
		else {
			temp[k] = arr[j];
			k++;
			j++;
		}
	}

	while (i <= mid) {
		temp[k] = arr[i];
		k++;
		i++;
	}

	while (j <= right) {
		temp[k] = arr[j];
		k++;
		j++;
	}

	for (int l = left; l <= right; l++) {
		arr[l] = temp[l - left];
	}
}

void MergeSort(int* arr, int left, int right) {
	if (left < right) {
		int mid = (left + right) / 2;
		MergeSort(arr, left, mid);
		MergeSort(arr, mid + 1, right);
		Merge(arr, left, mid, right);
	}
}

 

 

5. 퀵 정렬

분할정복 알고리즘의 하나로 pivot을 정해 그 값을 기준으로 원소들을 이동시키는 방식이다.

 

과정

  1. 데이터에서 임이의 원소 하나를 선택하여 pivot으로 정한다.
  2. pivot을 기준으로 왼쪽에는 pivot보다 작은 값을, 오른쪽에는 pivot보다 큰 값을 배치한다.
  3. pivot을 기준으로 왼쪽에서 다시 과정1과 과정2를 진행한다.
  4. 분할된 각 데이터의 크기가 0 또는 1이 되면 과정을 종료한다.

구현 (C++)

void swap(int* a, int* b) {
	int t = *a;
	*a = *b;
	*b = t;
}

int partition(int arr[], int low, int high) {
	int pivot = arr[high];
	int i = (low - 1);

	for (int j = low; j <= high - 1; j++) {
		if (arr[j] < pivot) {
			i++;
			swap(&arr[i], &arr[j]);
		}
	}
	swap(&arr[i + 1], &arr[high]);
	return (i + 1);
}

void quickSort(int arr[], int low, int high) {
	if (low < high) {
		int pivot = partition(arr, low, high);

		quickSort(arr, low, pivot - 1);
		quickSort(arr, pivot + 1, high);
	}
}

 

 

6. 힙 정렬

이란 완전 이진 트리를 기본으로 한 자료구조로 노드의 값이 자식의 키 값보다 작지 않은 경우 최대 힙, 크지 않은 경우를 최소 힙이라고 부른다.

힙 정렬은 최대 힙이나 최소 힙을 구성하여 정렬하는 방식이다.

 

과정

  1. 내림차순으로 정렬하려면 최대 힙을, 오름차순으로 정렬하려면 최소 힙을 구성한다.
    힙은 1차원 배열을 이용하여 구현한다.
    root부터 차례대로 depth를 올려가며 왼쪽에서 오른쪽으로 저장하면 된다.
  2. 힙의 root를 마지막 원소와 교환한 뒤, 힙의 크기를 1 줄인다.
  3. 새로운 힙이 다시 힙의 구조를 만족하도록 heapify라는 과정을 수행한다.
  4. 힙의 크기가 1이 될 때까지 과정2와 과정3을 반복한다.

구현 (C++)

void swap(int& a, int& b) {
	int t = a;
	a = b;
	b = t;
}

void heapify(int arr[], int n, int i) {
	int largest = i;
	int l = 2 * i + 1;
	int r = 2 * i + 2;

	if (l < n && arr[l] > arr[largest]) {
		largest = l;
	}

	if (r < n && arr[r] > arr[largest]) {
		largest = r;
	}

	if (largest != i) {
		swap(arr[i], arr[largest]);

		heapify(arr, n, largest);
	}
}

void heapSort(int arr[], int n) {
	for (int i = n / 2 - 1; i >= 0; i--) {
		heapify(arr, n, i);
	}

	for (int i = n - 1; i >= 0; i--) {
		swap(arr[0], arr[i]);

		heapify(arr, i, 0);
	}
}