← back to posts
·Algorithm

Algorithm/[Do it! 알고리즘 코딩테스트 with C++] 5장. 정렬

6개의 기본 정렬 알고리즘 학습하기

AlgorithmDo it 알고리즘 코딩테스트 C++

5-0 각 정렬 알고리즘의 정의

알고리즘정의
버블데이터의 인접 요소끼리 비교하고, swap 연산을 수행하며 정렬하는 방식
선택대상에서 가장 크거나 작은 데이터를 찾아 선택하는 과정을 반복하면서 정렬하는 방식
삽입대상을 선택해 정렬된 영역에서 선택 데이터의 적절한 위치를 찾아 삽입하면서 정렬하는 방식
피벗 값을 선정해 해당 값을 기준으로 정렬하는 방식
병합이미 정렬된 부분 집합들을 효율적으로 병합해 전체를 정렬하는 방식
기수데이터의 자릿수를 바탕으로 비교해 데이터를 정렬하는 방식

5-1 버블 정렬

버블정렬은 두 인접한 데이터의 크기를 비교해 정렬하는 방법이다.
간단하게 구현할 수 있지만 시간복잡도는 O(n^2)으로, 다른 정렬 알고리즘보다 속도가 느린 편이다.

다음과 같이 루프를 돌면서 인접한 데이터 간의 swap 연산으로 정렬한다.

버블 정렬 과정

  1. 비교 연산이 필요한 루프 범위를 설정한다.
  2. 인접한 데이터 값을 비교한다.
  3. swap 조건에 부합하면 swap 연산을 수행한다.
  4. 루프 범위가 끝날 때까지 2~3을 반복한다.
  5. 정렬된 영역을 설정한다. 다음 루프를 실행할때는 이 영역을 제외한다.
  6. 비교 대상이 없을 때까지 1~5를 반복한다

그리고 만약 특정한 루프의 전체 영역에서 swap 이 한번도 발생하지 않았다면 그 영역 뒤에 있는 데이터는 모두 정렬되었다는 뜻이므로 루프를 종료해도 된다.

문제 풀이

[문제 015] 수 정렬하기 1

시간 제한 1초, 브론즈 2, 백준 2750번
N개의 수가 주어졌을 때 이를 오름차순 정렬하는 프로그램을 작성하시오

sort() 함수를 사용해서 쉽게 정렬할 수 있지만, 정렬을 직접 구현해 해결해보는 문제다.

N의 최대 범위는 1,000으로 주어져있어 매우 작은데 비해 시간 제한은 1초로 꽤 널널하다. 따라서 O(n^2)의 시간복잡도로 문제를 풀어도 최대 연산 100만번, 1억 이내로는 매우 안정적이다.

의사코드 작성하기

N(정렬할 수 개수)
A(저장할 배열)

for (i는 0부터 N-1까지) {
  for (j는 0부터 N-1-i까지) {
    오름차순이니까 현재 A[j] > A[j+1] 이면 swap
  }
}
A 출력

풀이 코드

int main() {

	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	int N;
	cin >> N;
	vector<int> A(N, 0);

	for (int i = 0; i < N; i++) {
		cin >> A[i];
	}

	for (int i = 0; i < N - 1; i++) {
		for (int j = 0; j < N - 1 - i; j++) {
			if(A[j] > A[j + 1]) {
				int temp = A[j];
				A[j] = A[j + 1];
				A[j + 1] = temp;
			}
		}
	}

	for (int i = 0; i < N; i++) {
		cout << A[i] << "\n";
	}
    return 0;
}

5-2 선택 정렬

선택 정렬은 대상 데이터에서 최대나 최소 데이터를 나열된 순으로 찾아가며 선책하는 방법이다.
구현 방법이 비교적 복잡하고, 시간 복잡도도 O(n^2)으로 효율적이지는 않아 코테에서 많이 사용하지 않는 방법이긴 하다.

최솟값 혹은 최댓값을 찾고, 남은 정렬 부분의 가장 앞에 있는 값과 swap 하는 것이 핵심 아이디어다!

선택 정렬 과정

  1. 남은 정렬 부분에서 최솟값 혹은 최댓값을 찾는다.
  2. 남은 정렬 부분에서 가장 앞에 있는 값과 선택된 데이터를 swap 한다.
  3. 가장 앞에 있는 데이터의 위치를 변경해(index++) 남은 정렬 부분의 범위를 축소한다.
  4. 전체 데이터 크기만큼 index 가 커질 때까지, 즉 남은 정렬 부분이 없을 때까지 반복한다.

5-3 삽입 정렬

삽입 정렬은 이미 정렬된 데이터 범위에 정렬되지 않은 데이터를 적절한 위치에 삽입해 정렬하는 방식이다.
마찬가지로 시간 복잡도는 O(n^2)로 느린 편이지만 구현하기가 쉽다.

선택한 값을 현재 정렬된 범위 내에서 적절한 위치에 삽입하는 것이 핵심 아이디어다.

삽입 정렬 과정

  1. 현재 인덱스에 있는 값을 선택한다.
  2. 정렬된 범위에서 현재 선택한 값이 삽입될 위치를 탐색한다.
  3. 삽입 위치부터 인덱스에 있는 위치까지 shift 연산을 수행한다.
  4. 삽입 위치에 현재 선택한 값을 삽입하고 index++ 연산을 수행한다.
  5. 전체 데이터의 크기만큼 index가 커질 때까지, 즉 더이상 선택할 값이 없을 때까지 반복한다.

5-4 퀵 정렬

퀵 정렬은 기준값 pivot을 선정해 해당 값보다 작은 값과 큰 값으로 분류하는 것을 반복해 정렬하는 알고리즘이다.

피벗을 어떻게 선정하는지가 시간 복잡도에 많은 영향을 미치는데, 평균 시간복잡도는 O(nlogn) 이며 최악의 경우에는 O(n^2)이다.

피벗을 중심으로 계속 데이터를 2개의 집합으로 나누면서 정렬하는 것이 핵심 아이디어다!

5-5 병합 정렬

5-6 기수 정렬