본문 바로가기
WIL

1/8 WIL

by 달리는 꿈나무 2023. 1. 8.

1.  알고리즘 관련 공부

이번 주는 공부했던 알고리즘 지식을 정리하려 한다.

 

 

 

출처: https://gmlwjd9405.github.io/2018/05/08/algorithm-merge-sort.html

 

병합 정렬

  • 분할 정복 알고리즘 중 하나, 이분 탐색과 같은 투 포인터 전략을 사용
  • mid point를 찾아서 두 파트로 분할한다. mid = (lt+rt)//2
  • dfs와 같이 트리를 이용하는데 이진트리로 계속 정렬할 리스트를 나누는 것이다.
  • 기본적인 구성은 recursion 함수이다.
  • 특징은 분할 정복을 사용하기 때문에 두 파트로 나뉜다는 것이다.
  • 첫번째 파트는 분할이다. recursion의 특징을 이용하여 왼쪽 파트, 오른쪽 파트로 나눈다.
  • 두 번째 파트는 정복이다. 여기서는 함수 본연의 코드라고 보면 된다. 
  • 리스트는 콜 바이 레퍼런스가 되므로 temp라는 빈 리스트를 만들어 양 쪽 파트의 값들을 비교하면서 넣는다.
  • 여기서 만일 한 파트의 값들이 비교가 끝난 채로 남았다면 temp에 다 넣는다.
  • temp의 값들을 진짜 리스트에 넣는다. 단 0부터 넣는게 아니라 lt 즉, 왼쪽 포인터 부터 넣어야된다.
# 이분 탐색의 연장선
# 병합정렬, 왼쪽 인덱스와 오른쪽 인덱스
# 어레이1과 어레이2가 끊임없이 비교하는 거다.
def Dsort(lt,rt):
    if lt < rt:
        # 중간 값 찾기
        mid = (lt + rt)//2

        # 두 갈래로 뻗어 나누어진다.
        Dsort(lt,mid)
        Dsort(mid,rt)
# ========================== 여기까지 분할, 이 아래는 정복
#       이게 가능한 이유는 리스트의 콜 바이 레퍼런스 때문이다.


        # merge
        # 본연의 일 코딩 
        # - 여기가 merge 코드다.
        
        # 2분할한 왼쪽 자식
        p1 = lt

        # 2분할한 오른쪽 자식
        p2 = mid + 1

        # 임시 리스트 생성
        temp = []

        # 자식들이 한 바퀴 돌 때까지
        # 어느 한 쪽이 다 돌면 멈춰야한다.
        while p1 <= mid and p2 <= rt:
            if arr[p1] < arr[p2]:
                temp.append(arr[p1])
                p1+=1
            elif arr[p1] > arr[p2]:
                temp.append(arr[p2])
                p2+=1
        
        # p1이 남았다.
        if p1<=mid: # 남은 것들 다 넣자 mid 포함
            temp = temp + arr[p1:mid+1]
        # p2 남았다.
        if p2<=lt: # 남은 것들 다 넣자 lt 포함
            temp = temp + arr[p2:lt+1]
        # 정렬된 temp를 넘겨주어야한다.
        for i in range(len(temp)):
            # 조심 그냥 i에 넣어버리면 0으로 들어간다.
            # lt에 i를 더한 인덱스를 넣어주자
            # 다시 arr에 복사
            arr[lt + i] = temp[i]
        


if __name__ == "__main__":
    arr = [23,11,45,36,15,67,33,21]
    print("Before sort : ", end = "")
    print(arr)
    Dsort(0,7)
    print()
    print("After sort : ", end = "")
    print(arr)

 

 

 

 

 

출처: https://gmlwjd9405.github.io/2018/05/10/algorithm-quick-sort.html

 

퀵 정렬

  • 불안정 정렬이자, 비교 정렬, 퀵 정렬도 병합 정렬과 비슷하게 분할을 사용하지만 비균등 분할을 한다.
  • 병합 정렬과 똑같이 투 포인터 전략을 쓴다. lt와 rt
  • 병합 정렬은 선위 순회 방식(선 분할, 후 정렬)이지만 퀵 정렬은 후위 순회 방식(선 정렬, 후 분할)이다.
  • 피벗을 정하고 피벗을 기준으로 파티션을 만든다. 피벗은 보통 rt로 정한다.
  • 버블 정렬과 같이 for문을 통해 한 번 돌 때 정렬을 한 번 돌고 다음 파트로 나뉘어 넘어가서 정렬한다.
  • 본 함수의 로직이 끝나면 그 다음에 분할을 시작한다.
  • 스왑 로직은 pos라는 lt 부터 출발하는 추가 포인터가 필요하다.
  • 정렬을 돌 때 현재 위치의 값과 피벗을 비교해서 피벗 값이 연해 위치의 값보다 크면 pos값과 스왑하고 pos에 +1 한다.
  • for문이 끝나면 pos의 값과 피벗을 스왑한다.
  • 위 로직이 본 함수의 로직으로서 끝난다면 분할을 한다. 분할 기분은 pos로 lt부터 pos -1, pos+1부터 rt로 pos는 고정한다.
# 퀵정렬

# 병합 정렬은 선위 순회 방식
# 퀵정렬은 후위 순회 방식

# 병합 정렬은 먼저 나누고 그 다음 정렬
# 퀵정렬은 정렬하고 나눔

# 파티션 => 중심 값을 가지고 왼쪽과 오른쪽을 나눈다. == 분할

# 즉 병합정렬 => 선 파티션, 후 정렬
# 퀵정렬 => 선 정렬, 후 파티션

# 퀵정렬 또한 병합정렬과 같이 분할해서 생각한다.

# 퀵정렬은 피벗값을 지정해야한다. 피벗을 결정하는 데에 있어 성능 차이가 난다.
# 요새는 맨 오른쪽 값을 가장 많이 쓴다.
# 피벗값 == 중심값
# 피벗 값을 정하고 파티션을 진행
# pivot = arr[rt]


def QSort(lt, rt):
    if lt < rt:
        # lt 부터 rt 전까지
        pos = lt # pos 정하기, pos는 인덱스다.
        pivot = arr[rt] # pivot 정하기, pivot은 값이다.

        for i in range(lt,rt):
            # pivot 값과 arr[i]를 비교해서
            # 정렬을 시작한다. pivot 값이 arr[i]보다 크면
            # arr[pos]와 arr[i]의 값을 스왑
            # 스왑이 진행됐다면 pos에 1을 더해 인덱스를 증가시킨다.
            # pivot 값과 arr[i]를 다시 비교한다.
            if arr[i] <= pivot:
                temp = arr[i]
                arr[i] = arr[pos]
                arr[pos] = temp
                pos+=1
        
        # for문이 끝나면 arr[pos]와 pivot의 값을 스왑한다.
        temp = arr[pos]
        arr[pos] = pivot
        arr[rt] = temp
        
        # 이 다음에 분할! 재귀를 쓰자
        # DFS => 상태트리, 분할정렬은 => 분할 단계
        # pos는 가운데 기준이므로 포함하지 말자
        # 이미 기준을 세워서 나눴기 때문이다.
        QSort(lt, pos - 1)
        QSort(pos+1, rt)

        
            

if __name__ == "__main__":
    arr = [45, 21, 23, 36, 15, 67, 11, 60, 20, 33]
    print("Befoe sort : ", end = '')
    print(arr)
    # 처음 인덱스 부터 끝 인덱스까지 정렬
    QSort(0, len(arr)-1)
    print("After sort : ", end = '')
    print(arr)

 

 

 

 

'WIL' 카테고리의 다른 글

1/29 WIL  (0) 2023.01.31
1/22 WIL  (1) 2023.01.22
1/1 WIL  (0) 2023.01.01
12/25 WIL  (0) 2022.12.26
12/18 WIL  (0) 2022.12.18