[알고리즘] 정렬

MINO·2024년 8월 15일

정렬

데이터를 정해진 기준에 따라 배치해 의미 있는 구조로 재설정하는 것.

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

vector<int> v(n); // 정렬되지 않은 n개의 원소를 가진 벡터가 있다고 가정

버블 정렬

두 인접한 데이터의 크기를 비교해 정렬하는 방법.
시간 복잡도 : O(n^2)

for(int i = 0; i < n - 1; ++i)
{
	for(int j = 0; j < n - 1 - i; ++j)
   	{
   		if(v[j] > v[j + 1]) // 인접한 데이터의 크기를 비교
       	{
			int temp = A[j];
			A[j] = A[j + 1];
			A[j + 1] = temp;
		}
	}
}

선택 정렬

대상 데이터에서 최대나 최소 데이터를 나열된 순으로 찾아가며 선택하는 방법.
시간 복잡도 : O(n^2)

int idx,min;

for(int i = 0; i < n; ++i)
{
	min = INT_MAX;
    for(int j = i; j < n; ++j)
    {
    	if(v[j] < min)
        {
        	min = v[j];
            idx = j;
		}
	}
	
    int temp = v[i];
	v[i] = min;
    v[idx]  = temp;
}

삽입 정렬

이미 정렬된 데이터 범위에 정렬되지 않은 데이터를 적절한 위치에 삽입해 정렬하는 방법.
시간 복잡도 : O(n^2)

int key, idx;

for(int i = 1; i < n; ++i)
{
	key = v[i];
    for(int j = i - 1; j >= 0; --j)
    {
    	if(v[j] > key)
        	v[j+1] = v[j];
        else
        	break;
	}
    v[j+1] = key;
}

퀵 정렬

기준 값을 선정해 해당 값보다 작은 데이터와 큰 데이터로 분류하는 것을 반복해 정렬하는 방법.
평균 시간 복잡도 : O(n×log n), 최악 시간 복잡도 : O(n^2)

void quickSort(int start, int end) {
  if (start >= end)
  	return;
  int pivot = start;  // 기준 값
  int i = start + 1 , j = end;

  while (i <= j)
  {
    while (v[i] <= v[pivot])  // 키 값보다 큰 값 만날때까지 오른쪽으로 이동
      i++;
    
    while (v[j] >= v[pivot] && j > start)  // 키 값보다 작은 값 만날 때까지 왼쪽으로 이동
      j--;
    
    if (i > j)  //현재 엇갈린 상태면 pivot 값 교체
    {
      int temp = v[j];
      v[j] = v[pivot];
      v[pivot] = temp;
    }
    else
    {
      int temp = v[j];
      v[j] = v[i];
      v[i] = temp;
    }
    
    // 재귀 호출
    quickSort(start, j - 1);
    quickSort(j + 1, end);
  }
}

quickSort(0,n - 1); // 퀵 정렬

병합 정렬

분할 정복 방식을 이용해 데이터를 분할하고 분할한 집합을 정렬하며 합치는 방법.
시간 복잡도 : O(n×log n)

평균적으로 퀵 정렬보다 느리지만, 메모리를 많이 먹는다는 단점이 존재한다.

vector<int> v2;

// 병합하며 정렬
void merge(int left, int right) 
{
	int mid = (left + right) / 2;

	int i = left;
	int j = mid + 1;
	int k = left;
	while (i <= mid && j <= right)
	{
		if (v[i] <= v[j]) 
			v2[k++] = v[i++]; 
		else
			v2[k++] = v[j++];
	}

	int tmp = i > mid ? j : i;
	
	while(k<= right)
    	v2[k++] = v[tmp++];

	for (int i = left; i <= right; i++)
    	v[i] = v2[i];
}

// 분할을 재귀적으로 호출
void partition(int left,int right)
{
	int mid;
	if (left < right)
	{
		mid = (left + right) / 2; 
		partition(left, mid);
		partition(mid + 1, right);
		merge(left, right);
	}
}


partition(0,n-1); // 병합 정렬

기수 정렬

값을 놓고 비교할 자릿수를 정한 다음 해당 자릿수만 비교하는 방법.
시간 복잡도 : O(k×n) , k = 데이터의 자릿수

  • 방법 1 : N 개의 수가 주어졌을 때, 오름차순 정렬
    (수는 10,000 보다 작은 자연수)
int n;
cin >> n;

int cnt[10001] = {0};
int number = 0;

for(int i = 1; i <= n; ++i)
{
	cin >> number;
	cnt[number]++;
}

for(int i = 0; i <= 10000; ++i)
{
	if(cnt[i] != 0)
    {
    	for(int j = 0; j < cnt[i]; ++j)
        	cout << i << '\n';
	}
}

  • 데이터의 자리수가 2인 수열을 정렬.
  1. 10개의 큐를 이용하고, 각 큐는 값의 자릿수를 대표.
  2. 일의 자릿수를 기준으로 데이터를 저장.
  3. 일의 자릿수를 기준으로 데이터 정렬. (정렬된 순서 기준으로 십의 자리에 저장)
  4. 십의 자릿수를 기준으로 데이터 저장.
profile
안녕하세요 게임 개발하는 MINO 입니다.

0개의 댓글