[CS공부] 6. 정렬 알고리즘(2)

악어·2024년 3월 26일
0

CS / 알고리즘 공부

목록 보기
8/10
post-thumbnail

깃허브 정리본

용어

  • merge sort(병합 정렬): 배열을 앞/뒤 두그룹으로 나누어 각각 정렬한 후 병합하는 작업을 반복하는 알고리즘
    -> 안정적인 정렬 알고리즘, 시간 복잡도 O(nlogn)

  • heap(힙)=partial ordered tree(부분 순서 트리): '부모의 값이 자식의 값보다 항상 크다(작다)'는 조건을 만족하는 완전 이진트리
    -> 형제 간 대소관계는 정해져 있지 않음
    -> index를 매길땐 부모->자식 순, 왼쪽->오른쪽 자식 순으로 매김
    -> 따라서 a[i]의 주변 원소는 다음 관계를 가짐
    -> 부모: a[(i-1)//2], 왼쪽 자식: a[2*i+1], 오른쪽 자식: a[2*i+2]
    -> 힙에서 최댓값은 루트에 위치한다

  • heap sort(힙 정렬): 선택 정렬을 응용한 알고리즘으로, 힙의 루트를 꺼내고 나머지를 다시 힙으로 만드는 과정을 반복하는 알고리즘
    -> 불안정적인 정렬 알고리즘, 시간 복잡도 O(nlogn)

  • counting sort(도수 정렬)=distribution counting(분포수 세기): 원소의 대소 관계를 판단하지 않고 빠르게 정렬하는 알고리즘
    -> 도수분포표의 원리를 활용해서 정렬
    -> 원소를 비교할 필요가 없음
    -> if문을 사용하지 않고 for문만 반복해서 정렬할 수 있음
    -> 로직을 제대로 구현하면 안정적인 정렬 알고리즘, 시간/공간 복잡도 O(n)이지만 최대-최소값을 알고 있어야함



실습

병합 정렬

"""
기본 개념: 정렬할 배열을 두 그룹으로 나누고, 각 그룹 원소를 비교하고 새로운 배열에 저장하며 정렬.
=> 각 그룹의 원소 수가 1개가 될 때까지 나누는 과정을 재귀적으로 반복
"""
def _merge_sorted(lst_l: list, lst_r: list) -> list:
    pointer_l, pointer_r, pointer = 0, 0, 0
    n_l, n_r = len(lst_l), len(lst_r)
    lst = [0]*(n_l + n_r)

    if n_l > 1:
        lst_l = _merge_sorted(lst_l[:n_l//2], lst_l[n_l//2:])
    if n_r > 1:
        lst_r = _merge_sorted(lst_r[:n_r//2], lst_r[n_r//2:])
    
    while pointer_l < n_l and pointer_r < n_r:
        if lst_l[pointer_l] <= lst_r[pointer_r]:
            lst[pointer] = lst_l[pointer_l]
            pointer_l += 1
        else:
            lst[pointer] = lst_r[pointer_r]
            pointer_r += 1
        pointer += 1

    while pointer_l < n_l:
        lst[pointer] = lst_l[pointer_l]
        pointer_l += 1
        pointer += 1

    while pointer_r < n_r:
        lst[pointer] = lst_r[pointer_r]
        pointer_r += 1
        pointer += 1

    return lst


def merge_sorted(lst: list) -> list:
    n = len(lst)
    return _merge_sorted(lst[:n//2], lst[n//2:])

lst_1 = [1, 3, 9, 4, 11, 7, 88, 8, 6]

print(merge_sorted(lst_1))


# 실행결과 ==========================================
[1, 3, 4, 6, 7, 8, 9, 11, 88]

힙 정렬

"""
기본 개념: 정렬할 배열을 힙으로 만들고, 힙에서 루트값을 순서대로 나열하는 식으로 정렬 진행
"""
def make_heap(lst: list) -> list:
    temp = lst[0]
    
    n = len(lst)
    parent = 0
    while parent < n//2:
        child_l = parent * 2 + 1
        child_r = child_l + 1
        child = child_r if child_r < n and lst[child_r] > lst[child_l] else child_l
        if temp >= lst[child]:
            break
        lst[parent] = lst[child]
        parent = child
    lst[parent] = temp

    return lst


def heap_sorted(lst) -> list:
    n = len(lst)

    for i in range((n-1)//2, -1, -1):
        lst[i:n] = make_heap(lst[i:n])

    for i in range(n-1, 0, -1):
        lst[0], lst[i] = lst[i], lst[0]
        lst[0:i] = make_heap(lst[0:i])

    return lst


lst_1 = [1, 3, 9, 4, 11, 7, 88, 8, 6]

print(heap_sorted(lst_1))


# 실행결과 ==========================================
[1, 3, 4, 6, 7, 8, 9, 11, 88]

도수 정렬

"""
기본 개념: 누적 도수분포표를 만들고, 표의 인덱스에따라 재배치
"""
def counting_sorted(lst: list) -> list:
    M = max(lst)
    n = len(lst)
    f = [0] * (M + 1)
    new_lst = [0] * n
    
    for i in range(n): f[lst[i]] += 1
    for i in range(1, M+1): f[i] += f[i-1]
    for i in range(n-1, -1, -1): f[lst[i]] -= 1; new_lst[f[lst[i]]] = lst[i]

    return new_lst


lst_1 = [1, 3, 9, 4, 11, 7, 88, 8, 6]

print(counting_sorted(lst_1))


# 실행결과 ==========================================
[1, 3, 4, 6, 7, 8, 9, 11, 88]


정리

드디어 8가지 정렬 알고리즘의 정리가 끝났다.

개인적으로 복잡한 재귀나 while문 없이 구현할 수 있는
도수 정렬이 가장 활용하기 좋아보였지만,
배열의 최대/최솟값 간격이 클수록
공간 복잡도가 커져 비효율적일 것 같았다.
필요에 따라 적절한 정렬 방법을 사용해야할 듯하다.

힙 정렬의 경우, 힙 구조를 1차원 배열로 표현하다보니
구현 로직이 잘 이해가지 않았다.
몇 번씩 구현해보며 손에 익혀야 할 듯하다.

마지막으로 8가지 정렬알고리즘을 표로 정리해보자.


시간 복잡도공간 복잡도안정성특징
버블 정렬O(n^2)O(n)o알고리즘이 매우 단순함
단순 선택 정렬O(n^2)O(n)x소수의 데이터를 추가하고 다시 정렬할 때 효율이 매우 떨어짐
단순 삽입 정렬O(n^2)O(n)o삽입 위치에 따라 이동 횟수가 많아질 수 있음
셸 정렬O(n^1.25)O(n)x그룹을 어떻게 나누냐에 따른 정렬 효율 차이가 심함
퀵 정렬O(nlogn)O(n)x일반적으로 많은 데이터에 대해 가장 빠르다고 알려진 방법
병합 정렬O(nlogn)O(2*n)o임시 배열이 필요해 추가 메모리가 요구됨
힙 정렬O(nlogn)O(n)x구현이 복잡함
도수 정렬O(a*n)O(a*n)o효율적인 정렬이 가능하지만, 최솟값/최댓값에 따라 사용 가능 여부가 결정됨.


profile
냅다 회사부터 세워버린 개발자

0개의 댓글