소프트웨어 공부/자료구조

[자료구조] 퀵 정렬

야곰야곰+책벌레 2021. 12. 27. 14:13
728x90
반응형

퀵 정렬 알고리즘에서 분할은 매우 중요하다.

 

분할

배열을 분할한다는 것은 배열로부터 임의의 수를 가져와(이 수를 피벗이라고 부름) 피벗보다 작은 모든 수는 피벗의 왼쪽에, 피벗보다 큰 모든 수는 피벗의 오른쪽에 두는 것이다. 아래와 같은

stormpy.tistory.com

퀵 정렬 알고리즘은 아래와 같이 동작한다.

  1. 배열을 분할한다. 피벗은 이제 올바른 위치에 있다.
  2. 피벗의 왼쪽과 오른쪽에 있는 하위 배열을 각각 또 다른 배열로 보고 1단계와 2단계를 재귀적으로 반복한다.
  3. 하위 배열이 원소를 0개 또는 1개 포함하면 기저 조건이므로 아무것도 하지 않는다.
#include <vector>
#include <iostream>
using namespace std;

class CSortArray
{
public:
	CSortArray() = default;
	CSortArray(vector<int>& arr)
	{
		array = arr;
	}

	int partition(int lptr, int rptr)
	{
		int pivot_pos = rptr;
		int pivot = array[pivot_pos];
		rptr--;

		while (true)
		{
			while (array[lptr] < pivot)
				lptr++;
			while (array[rptr] > pivot)
				rptr--;

			if (lptr >= rptr)
				break;
			else
				swap(lptr, rptr);
		}

		swap(lptr, pivot_pos);
		return lptr;
	}

	void swap(int ptr1, int ptr2)
	{
		int tmp_value = array[ptr1];
		array[ptr1] = array[ptr2];
		array[ptr2] = tmp_value;
	}

	vector<int> array;
};

class CQuickSort : public CSortArray
{
public:
	CQuickSort() = default;
	CQuickSort(vector<int>& arr) : CSortArray(arr) {};

	void quicksort(int leftIndex, int rightIndex)
	{
		if (rightIndex - leftIndex <= 0)
			return;

		int pivot_pos = partition(leftIndex, rightIndex);
		quicksort(leftIndex, pivot_pos - 1);
		quicksort(pivot_pos + 1, rightIndex);
	}
};

int main()
{
	vector<int> testArr = { 0, 5, 2, 1, 6, 3 };
	CQuickSort quickSort(testArr);
	quickSort.quicksort(0, testArr.size() - 1);

	for (int i = 0; i < (int)testArr.size(); i++)
		cout << testArr[i] << "\t";
	cout << endl;
	for (int i = 0; i < (int)quickSort.array.size(); i++)
		cout << quickSort.array[i] << "\t";
	cout << endl;

	return 0;
}

분할을 했을 때에는 pivot의 위치의 정확성만 보았다면, 퀵 정렬에서는 정렬까지 이뤄졌다. 

 

퀵 정렬의 효율성을 알아내려면 먼저 분할의 효율성을 밝혀야 한다. 분할에 필요한 단계를 분류해 보면 단계가 두 종류임을 알 수 있다.

  • 비교 : 각 값과 피벗을 비교한다.
  • 교환 : 적절한 때에 왼쪽과 오른쪽 포인터가 가리키고 있는 값을 교환한다.

각 분할마다 배열 내 각 원소를 피벗과 비교하므로 최소 N번 비교한다. 분할을 한 번 할 때마다 왼쪽과 오른쪽 포인터가 서로 만날 때까지 각 셀을 이동하기 때문이다. 교환 횟수는 최대 N/2번 교환한다.

 

평균적인 상황에서 퀵 정렬이 우수하기 때문에 내부적으로 퀵 정렬을 사용해 자신만의 정렬 함수를 구현하는 언어가 많다. 따라서 퀵 정렬을 직접 구현할 일은 거의 없다. 하지만 퀵 셀렉트라 불리는 매우 유사한 알고리즘이 실용적으로 쓸모가 있을 수 있다.

728x90
반응형

'소프트웨어 공부 > 자료구조' 카테고리의 다른 글

[자료구조] 연결 리스트(linked list)  (0) 2021.12.27
[자료구조] 퀵 셀렉트  (0) 2021.12.27
[자료구조] 분할  (0) 2021.12.27
[자료구조] 재귀(recursion)  (0) 2021.12.27
[자료구조] 큐(Queue)  (2) 2021.12.24