TIL

[병합 정렬, 퀵 정렬, 이진 탐색, 백트래킹, 트리]

Hoo_Dev 2022. 3. 31. 00:09

TIL_0330

MergeSort

반으로 쪼개고-> 나중에 합쳐서 정렬!

그림처럼 반으로 계속해서 쪼개다보면 결국 1개의 원소만 남고 이들을 비교하여 작은 값 부터 새로운 리스트에 저장을 하는 방식.

def merge(left, right):
    lp = rp = 0
    result = []

    while lp < len(left) and rp < len(right):
        if left[lp] < right[rp]:
            result.append(left[lp])
            lp += 1
        else:
            result.append(right[rp])
            rp += 1

    while lp < len(left):
        result.append(left[lp])
        lp += 1

    while rp < len(right):
        result.append(right[rp])
        rp += 1

    return result

def mergeSort(lst):
    if len(lst) <= 1:
        return lst

    # 중간 지점을 계산
    m = len(lst) // 2
    left = mergeSort(lst[:m])
    right = mergeSort(lst[m:])
    result = merge(left, right)
    return result

lst = [69, 10, 30, 2, 16, 8, 31, 22 ]
result = mergeSort(lst)
print(result)

QuickSort

Hoare-Partition 알고리즘
quickSort(A[], L, r)
    if L < r
        s <- partition(a, L, r)
        quickSort(A[], L, s-1)
        quickSort(A[], s+1, r)
quickSort 시작 부분
partition(A[], L, r)
    p <- A[L]       // p : 피봇 값
    i <- L, j <- r
    while i <= j
        while i < j and A[i] <= p : i++
        while i < j and A[j] >= p : j--
        if i < j : swap(A[i], A[j])

    swap(A[L], A[j])
    return j
피봇값 (위 알고리즘은 맨 왼쪽 값을 피봇으로)

피봇값은 왼쪽 끝, 가운데, 맨 오른쪽 으로 잡을 수 있다. (세 값을 아무거나 잡고 그 중 가운데 값으로 삼기도 함)

 

while i, j

i는 맨 왼쪽 값(피봇보다 큰 값을 찾을 때 까지 증가), j는 맨 오른쪽 값(피봇보다 작은 값을 찾을 때 까지 감소) 부터 시작해서 서로 교차하기 전까지 반복.

교차되지 않은 상태에서 if i < j 라면 그 둘의 자리를 바꾼다 (여기서 swap은 함수가 아님)
교차 했다면 while을 빠져나오면서 swap.

 

return j

자리잡은 피봇의 위치를 return

quickSort (A[], L, r)
    if L < r
        s <- partition(a, L, r)
        quickSort(A[], L, s-1)
        quickSort(A[], s+1m r)
재귀 quickSort()

quickSort를 다시 불러옴으로써 조정 된 피봇 위치를 통해 지속적으로 정렬을 진행해 줌

결론

결국 왼쪽은 이미 정렬 된 상태이므로 변동이 없이 오른쪽을 진행. 재귀적으로 진행하다보면 최종적으로 sort된 배열이 완성.


Lomuto partition 알고리즘
partition(A[], p, r)
    x <- A[r]
    i <- p - 1
    for j in p -> r-1
        if A[j] <= x
            i++, swap(A[i], A[j])
    swap(A[i+1], A[r])
    return i+1

x <- A[r]

맨 오른쪽 값이 기준

i <- p - 1

첫 값보다 뒤에(j보다 하나 뒤)

i, j (Hoare-Partition 알고리즘과 달리 i, j가 한 방향으로 움직임)

A[j] 가 x보다 같거나, 작으면 i가 따라감.(i, j가 같이 1씩 증가하고 자리를 서로 바꿈)

x보다 큰 값을 만나면 i가 따라오지 않고 남아있음 즉, j 만 이동 (*i 다음 위치가 피봇보다 큰 애 의 시작 위치)

그림 예시에서 5번째 줄은 j가 작은애를 만났으므로 i를 증가시키고 그 둘을 바꿔줌 (swap(A[i], A[j]))

swap(A[i+1], A[r])

for문이 완료 된다면 위와 같이 멈춰있는 i보다 1큰 위치랑 바꿔줌 (*참고)

이진검색

검색과정

  1. 자료의 중앙에 있는 원소를 고른다.
  2. 중앙 원소의 값과 찾고자 하는 목표 값을 비교한다.
  3. 목표 값이 중앙 원소의 값보다 작으면 자료의 왼쪽 반에 대해서 새로 검색을 수행하고, 크다면 자료의 오른쪽 반에 대해서 새로 검색을 수행한다.
  4. 찾고자 하는 값을 찾을 때 까지 1~3의 과정을 반복한다.
#pseudocode
binarySearch(n, s[], key) # 파이썬이 아닌 다른 언어는 배열도 넘겨줘야 됨
low <- 0
high <- n - 1
while low <= high # 키 값이 같은 경우에도 고려해야한다.
    mid <- low + (high - low) / 2 # (high - low) = n

    if S[mid] == key
        return mid
    elif S[mid] > key
        high <- mid - 1
    else
        low <- mid + 1
return -1
# 알고리즘 : 재귀구조
binarySearch(a[], low, high, key)
    if low > high
        return -1
    else
        min <- (low+high)/2
        if key == a[mid]
            return mid
        elif key < a[mid]
            return binarySearch(a[], low, mid-1, key)
        else
            return binarySearch(a[], mid + 1, high, key)

백트래킹

  • 불필요한 경로를 초기에 차단
  • 가지치기를 통해 시도의 횟수를 줄임 (Prunning : 가지치기)
  • 최악의 경우에는 여전히 지수함수 시간을 요하므로 처리 불가능

ex) 8-Queens

후보 해의 수 64C8 = 4,426,165,368

실제 해의 수 : 92개

즉, 44억개가 넘는 후보 해의 수 속에서 92개를 최대한 효율적으로 찾아내야 함.

트리

이진 트리

전위 순회 : V L R

preorder_traverse(TREE T)
    if T is not null
        visit(T)
        preorder_traverse(T.left)
        preorder_traverse(T.right)

중위 순회 : L V R

inorder_traverse (TREE T)
    if T is not null
        inorder_traverse(T.left)
        visit(T)
        inorder_traverse(T.right)

후위 순회 : L R V

postorder_traverse (TREE T)
    if T is not null
        postorder_traverse(T.left)
        postorder_traverse(T.right)
        visit(T)