알고리즘(코딩테스트)

버블, 선택, 삽입 정렬 + 퀵 정렬

gbleem 2025. 2. 25. 11:52

업데이트) 병합 정렬과 이분탐색 알고리즘 추가

자세한 예시는 해당 글에 있고, 전반적인 정리는 아래 링크의 글을 참고하면 된다.

https://gbleem.tistory.com/128

 

정렬 및 탐색 알고리즘 정리

1. 정렬 알고리즘1 - 1. 버블정렬배열에서 두개의 원소 선택하고 비교 후, 왼쪽 원소가 오른쪽 원소보다 큰 경우 swap 한다.시간 복잡도는 O(N^2), 공간 복잡도는 O(1)만약 아래 코드에서 swap이 발생하

gbleem.tistory.com

 

1. 버블 정렬


개념

  • 앞에서부터 시작해서 자기 자신과 하나 뒤에 값을 비교하는데, 자신보다 작은 값이 나온 순간 swap을 해주는 과정을 통해 정렬한다.
  • 한바퀴 순환을 하고나면, 맨 뒤의 값이 가장 큰 값이 된다.
  • N개의 데이터가 있을 때, 이중 for문을 모두 순환해야 하므로 O(N^2)의 시간 복잡도를 가진다.

예시

  • 4 6 2 9 1 이라는 값이 있다고 할 때
  • 첫번째 순환에 있어서는 4 6 2 9 1 -> 4 2 6 9 1 -> 4 2 6 1 9 으로 바뀌고
  • 두번째 순환에서는 4 2 6 1 9 -> 2 4 6 1 9 -> 2 4 1 6 9 로 바뀐다.
  • 세번째 순환에서는 2 4 1 6 9 -> 2 1 4 6 9
  • 마지막 순환에서 2 1 4 6 9 -> 1 2 4 6 9 로 정렬이 완료된다.

코드

//Bubble Sort

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> vec = { 4,6,2,9,1 };

	for (int i = 0; i < vec.size(); ++i)
	{
		for (int j = 0; j < vec.size() - i - 1; ++j)
		{
			if (vec[j] > vec[j + 1])
				swap(vec[j], vec[j + 1]);
		}
	}
	for (const auto v : vec)
		cout << v << " ";
}

 

 

 

 

2. 선택 정렬


개념

  • 앞에서부터 시작해서, 자신의 자리의 값과 나머지 값들을 모두 비교해서, 가장 작은 값을 자신과 swap 하는 과정을 통해 정렬한다.
  • 한바퀴 순환을 하고나면, 맨 앞의 값이 가장 작은 값이 된다.
  • 시간 복잡도는 O(N^2)이다.

예시

  • 4 6 2 9 1 이라는 값이 있을 때
  • 첫번째 순환으로 1 6 2 9 4
  • 두번째 순환으로 1 2 6 9 4
  • 세번째 순환으로 1 2 4 9 6
  • 네번째 순환으로 1 2 4 6 9 정렬 완료

코드

//selection sort

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> vec = { 4,6,2,9,1 };

	for (int i = 0; i < vec.size() - 1; ++i)
	{
		int idx = i;
		for (int j = i + 1; j < vec.size(); ++j)
		{
			if (vec[j] < vec[idx])
				idx = j;
		}
		
		swap(vec[i], vec[idx]);			
	}
	for (auto v : vec)
		cout << v << " ";	
}

 

 

3. 삽입 정렬


개념

  • 앞에서부터 시작하는데 (해당 인덱스를 i라고 하면) 다음 값(i + 1)과 비교해서 만약 i+1의 값이
    • 더 작은 경우 0 ~ i 까지의 값과 모두 비교하면서 i+1에 해당하는 값보다 작은 값 뒤로 위치시켜주면서 정렬하는 방식이다.
    • 더 큰 경우 i를 하나 증가시켜 반복한다.
  • 삽입 정렬은 O(N^2)의 시간 복잡도 이지만, 최선의 경우(정렬된 경우) O(N)의 복잡도가 된다.

예시

  • 4 6 2 9 1이 있을때
  • 4를 6과 비교, 6이 4 보다 크기 때문에 i 증가
    • 4 6 2 9 1
  • 6과 2를 비교
    • 2가 6보다 작으므로 swap
    • 2가 4보다 작으므로 swap
    • 2 4 6 9 1
  • 9와 6을 비교 (2는 맨앞으로 이동)
    • 9가 6 보다 크므로 i 증가
  • 1과 9를 비교
    • 1은 모든 숫자보다 작으므로
    • 계속 swap해서 맨 앞으로 이동
    • 1 2 4 6 9 정렬 완료

코드

//insertion sort
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> vec = { 4,6,2,9,1 };

	int value, j;
	for (int i = 1; i < vec.size(); i++) 
	{ 
		value = vec[i];
		for (j = i - 1; j >= 0; j--) 
		{
			if (vec[j] > value)
				swap(vec[j + 1], vec[j]);
			else
				break;
		}
	}
	for (auto v : vec)
		cout << v << " ";
}

 

 

4. 퀵 정렬


개념

  • 피벗이라는 기준점을 기반으로 피벗의 왼쪽은 작은값을 오른쪽은 큰 값으로 나눠서 정렬하는 방식
  • 정렬하고자하는 배열을 나눠서 각자 나눠진 배열을 정렬하여 전체 배열을 정렬하게 된다.
  • 최적(평균)의 경우 O(NlogN) 이지만 최악의 경우 O(N^2)

예시

  • 4 6 2 9 1이 있다고 할때, low는 0 high는 4이다
  • 첫번째 Partition 함수를 실행하게 되면,
    • mid = 2(인덱스), swap을 통해 중앙값을 맨 뒤로 보내면 4 6 1 9 2 가 된다.
    • pivot은 2(값) 이고 idx는 -1부터 시작하게 된다.
    • for문
      • 4와 6은 2(피벗)보다 크므로 아무 동작 x
      • 1은 2보다 작으므로 idx++ 한 후 swap -> 1 6 4 9 2
      • 9는 마찬가지로 2보다 크므로 아무동작 안하고 for문 종료
      • 2라는 피벗값 (인덱스는 high)을 2보다 작은 값들이 있는(0 ~ idx) 위치의 다음 위치와 swap하기 -> 1 2 4 9 6
    • 이후 1이라는 피벗 위치를 return
  • Partition함수가 끝나고 pos는 1이 된다.
  • 이후 QuickSort 재귀함수 호출을 통해
    • QuickSort(vec, 0, 0)과 QuickSort(vec, 2, 4)를 실행하게 될 것인데
    • QuickSort(vec, 0, 0)의 경우 low < high 를 만족하지 않기에 종료 (정렬되었다는 뜻)
    • QuickSort(vec, 2, 4)만 실행하면 된다.
  • 두번째 Partition 함수는
    • mid = 3, swap을 통해 1 2 4 6 9 가 된다.
    • pivot은 9이고 idx 는 1에서 시작
    • for문
      • 4(i = 2)는 9(피벗)보다 작으므로 idx++ (idx = 2)하고 swap -> 1 2 4 6 9 그대로
      • 6(i = 3)도 9보다 작으므로 idx++ (idx = 3) 하고 swap -> 1 2 4 6 9 그대로고 for문 종료
    • idx는 3인데 idx+1하고 high값과 swap한 후(그대로) idx + 1값인 4를 return  
  • 이후 QuickSort(vec, 2, 4) 을 한번 실행하고 Partition(vec, 2, 3)을 통해 2를 return
  • 이후 QuickSort가 low < high 조건을 만족하는 경우가 없어 종료된다.

코드

// quick sort

#include <iostream>
#include <vector>
using namespace std;

int Partition(vector<int>& vec, int low, int high)
{
	//중간값 찾기
	int mid = (low + high) / 2;

	//중간값을 맨 값이랑 swap
	swap(vec[mid], vec[high]);

	//중간값을 pivot으로 설정
	int pivot = vec[high];

	//시작 인덱스 설정
	int idx = low - 1;

	for (int i = low; i < high; ++i)
	{
		//피벗보다 작은 값을 왼쪽으로 몰아버리기
		if (vec[i] < pivot)
		{
			idx++;
			swap(vec[i], vec[idx]);
		}
	}
	//피벗을 정렬한 값 중앙으로 옮겨주기
	swap(vec[idx + 1], vec[high]);

	//정렬한 vec의 피벗 위치 return
	return idx + 1;
}

void QuickSort(vector<int>& vec, int low, int high)
{
	if (low < high)
	{
		//피벗보다 작은 값을 왼쪽 큰 값을 오른쪽으로 바꿔주기
		int pos = Partition(vec, low, high);

		//피벗을 기준으로 재귀함수 시작
		QuickSort(vec, low, pos - 1);
		QuickSort(vec, pos + 1, high);
	}
}


int main()
{
	vector<int> vec = { 4, 6, 2, 9, 1 };

	int low = 0;
	int high = vec.size() - 1;

	QuickSort(vec, low, high);

	for (auto v : vec)
		cout << v << " ";
}