본문 바로가기

코딩 및 기타/어서와! 자료구조와 알고리즘

16 일차

728x90

정렬과 탐색 (분할 정복과 정렬 알고리즘)

∙ 분할 정복 (divide and conquer)

- 주어진 문제의 규모가 커서 한 번에 해결하기 어려운 경우, 이를 작은 부분 문제로 나워서 해결하는 방식

- 분할(divide) : 주어진 문제를 동일한 방식으로 해결할 수 있는 부분 문제로 나누기

- 정복(conquer) : 각 부분 문제에 대한 솔루션 구하기

- 결합(combine) : 각 부분 문제의 솔루션을 결합하여 전체 문제에 대한 솔루션 구하기

 

∙ 분할 정복을 이용한 정렬 알고리즘

- 병합 정렬(merge sort)

> 분할 정복에 의한 정렬 알고리즘의 하나

> 입력 배열을 두 개의 부분 배열로 분할

    > 각 부분 배열의 원소가 하나가 될 때까지 반복

> 부분 배열을 병합

    > 이때 병합된 배열의 원소가 정해진 정렬 순서에 부합되도록 순서를 조정

 

- 병합 정렬의 시간 복잡도

> 배열을 분할 또는 병합하는 단계 : log n

> 배열 병합하는 과정의 시간 복잡도 : O(n)

> 전체 시간 복잡도 : O(n log n)

 

- 병합 정렬의 특징

> 실제 정렬이 이루어지는 시점은 두 개의 부분 배열이 병합하는 단계임

> 안정된 정렬이며 데이터의 초기 순서에 영향을 크게 받지 않음

> 병합 과정에서 임시 배열을 필요로 함 (공간 복잡도 : O(n))

 

- 병합 정렬 코드

#include <iostream>

using namespace std;

// int buff[256]; 로 전역변수로 할당함
int buff[256];

// 병합 함수
void merge(int data[], int left, int mid, int right)
{
    int i = left, j = mid + 1, k = left;
    // 한개씩 비교 하는 코드
    while (i <= mid && j <= right){
        if (data[i] <= data[j])
            buff[k++] = data[i++];
        else
            buff[k++] = data[j++];
    }
    
    while (i <= mid)
        buff[k++] = data[i++];
    while (j <= right)
        buff[k++] = data[j++];
    // 정렬된 데이터 복사하기
    for (int x = left; x <= right; x++)
        data[x] = buff[x];
}

void merge_sort(int data[], int left, int right)
{
    if (left < right) {
        int mid = (left + right) / 2;
        merge_sort(data, left, mid);
        merge_sort(data, mid + 1, right);
        // 병합
        merge(data, left, mid, right);
    }
}

int main()
{
    int data[] = {2, 6, 7, 4, 1, 8, 5, 3};
    merge_sort(data, 0, 7);
    
    for (auto n : data)
        cout << n << ", ";
    cout << endl;
}

 

 

- 퀵 정렬(quick sort)

> 병합 정렬과 마찬가지로 분할 정복에 의한 정렬 알고리즘

> 특정 원소를 피벗으로 선택하고, 주어진 배열을 피벗보다 작은 부분과 큰 부분으로 분할

    > 피벗 : 비교 기준 원소

> 분할된 부분 배열에 대해 재귀적으로 퀵 정렬 분할 작업을 반복

 

- 퀵 정렬의 시간 복잡도

> 최선의 경우 : O(n log n) (항상 절반으로 분할되는 경우)

> 최악의 경우 : O(n^2) (한 쪽은 1개, 다른 쪽은 n-1개로 분할되는 경우)

 

- 퀵 정령의 특징

> 실제 정렬이 이루어지는 시점은 배열을 피벗 기준으로 분할하는 단계임

> 안정되지 않은 정렬이며 데이터의 초기 순서의 영향을 크게 받음

> 평균적으로 가장 빠른 정렬 알고리즘 -> C/C++ 라이브러리로 기본 제공되는 정렬 알고리즘

 

#include <iostream>
#include <algorithm>

using namespace std;

int partition(int data[], int left, int right)
{
    int pivot = data[left];
    int i = left + 1, j = right;
    
    while (true){
        // i를 증가시키면서 pivot보다 큰 data[i] 선택
        while (data[i] <= pivot && i <= right)
            i++;
        // j를 감소시키면서 pivot보다 작은 data[i] 선택
        while (data[j] > pivot && j > left)
            j--;
        // 만약 i < j 이면 data[i]와 data[j]를 교환하고, 1) i를 증사키는 코드부터 다시 시작
        if (i < j)
            swap(data[i], data[j]);
        // 그렇지 않으면 pivot과 data[j]를 교환
        else
            break;
    }
    swap(data[left], data[j]);
    return j;
}

void quick_sort(int data[], int left, int right)
{
    if (left < right){
        // parition 함수는 피벗 기준으로 배열을 재배치하는 함수이다.
        int p = partition(data, left, right);
        quick_sort(data, left, p - 1);
        quick_sort(data, p + 1, right);
    }
}

int main()
{
    int data[] = {5, 6, 7,3, 1, 9, 2, 4,8};
    // data배열, 배열의 첫번째 인덱스에 해당하는 0을 입력으로 줌, data의 크기 9 - 1
    quick_sort(data, 0, size(data) - 1);
    
    for (auto n : data)
        cout << n << ", ";
    cout << endl;
}

- 힙 정렬(heap sort)

'코딩 및 기타 > 어서와! 자료구조와 알고리즘' 카테고리의 다른 글

18 일차  (0) 2023.08.26
17 일차  (0) 2023.08.25
15 일차  (0) 2023.08.24
재귀 알고리즘 주요 예제  (0) 2023.08.22
14 일차  (0) 2023.08.22