Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4️⃣ 정렬 #4

Open
heydoy opened this issue May 27, 2022 · 5 comments
Open

4️⃣ 정렬 #4

heydoy opened this issue May 27, 2022 · 5 comments

Comments

@heydoy
Copy link
Member

heydoy commented May 27, 2022

정렬 알고리즘 개요

  • 정렬 Sorting이란 데이터를 _특정 기준에 따라 순서대로 나열_하는 것 (e.g. 오름차순, 내림차순)
  • 정렬 알고리즘으로 데이터를 정렬하면 이진탐색 Binary Search이 가능하다. 이진 탐색의 전처리 과정도 되는 셈
  • 파이썬에서는 특정 리스트의 원소를 뒤집는 메서드를 제공하므로, 내림차순 정렬은 오름차순 정렬을 수행한 뒤에 결과를 뒤집기 하여 내림차순 리스트를 만들 수 있다. 리스트를 뒤집는 연산은 O(N)의 복잡도
@heydoy
Copy link
Member Author

heydoy commented Jul 9, 2022

선택정렬

가장 작은 데이터를 선택해 맨 앞에 있는 데이터와 바꾸고, 그 다음 작은 데이터를 선택에서 두번째 데이터와 바꾸는... 과정을 반복하는 것. 가장 원시적인 방법으로 매번 '가장 작은 것을 선택'한다는 의미에서 선택 정렬 Selection Sort 알고리즘이라고 한다.

array = [3, 7, 1, 0, 2, 8, 9]

for i in range(len(array)):
    min_index = i
    for j in range(i+1, len(array)):
        if array[min_index] > array [j] : 
            min_index = j
    array[i], array[min_index] = array[min_index], array[i] #위치 Swap 

다른 대부분의 프로그래밍 언어에서 명시적으로 임시저장용 변수를 만들어 원소의 값을 변경해야 하지만 파이썬에서는 단순히 원소값을 변경할 수 있다.

a, b = b, a
  • 선택정렬의 시간복잡도
    O(N^2)
    선택정렬을 이용하는 경우 데이터의 개수가 10,000개 이상이면 정렬속도가 급격히 느려진다.

@heydoy
Copy link
Member Author

heydoy commented Jul 9, 2022

삽입정렬

삽입정렬은 특정 데이터를 적절한 위치에 삽입한다는 의미에서 삽입 정렬 Insertion sort 라고 부른다. 데이터를 하나씩 확인하여 필요할 때만 위치를 바꾸는 정렬로 데이터가 '거의' 정렬되어있을 때 훨씬 효율적이다. 반면 선택 정렬은 데이터 상태와 상관없이 모든 원소를 비교하고 정렬한다.

삽입정렬은 특정 데이터가 들어가는 적절한 위치 앞까지의 데이터는 이미 정렬되었다고 가정한다. 때문에 삽입 정렬은 두번째 데이터부터 어떤 위치에 들어갈 지 판단한다.

array = [3, 7, 1, 0, 2, 8, 9]

for i in range(1, len(array)) : 
    for j in range(i, 0, -1) : 
        if array[j] < array[j-1] : 
            array[j], array[j-1] = array[j-1], array[j]
        else : 
            break
  • 삽입정렬의 시간 복잡도
    선택정렬과 마찬가지로 반복문이 두번 중첩되어 O(N^2)이 된다.
    만약 현재 리스트의 데이터가 거의 정렬되어 있다면 매우 빠르게 동작하며, 최선의 경우 O(N)의 시간복잡도를 가진다.

보통은 퀵 정렬이 빠르고 강력한 알고리즘이지만 정렬이 거의 된 상황이라면 퀵 정렬 알고리즘보다 삽입정렬이 강력하다.

@heydoy
Copy link
Member Author

heydoy commented Jul 9, 2022

퀵정렬

퀵정렬과 병합정렬 알고리즘은 정렬 알고리즘 중 가장 많이 사용되는 알고리즘이다. 기준을 설정한 다음 기준보다 큰 수와 작은 수를 교환해서 리스트를 반으로 나누는 방식으로 동작한다.

퀵 정렬에서는 피봇 Pivot이 사용되는데, 위에서 설명한 '기준'을 피봇이라고 표현한다. 퀵 정렬을 수행하기 전에 피봇을 어떻게 설정할 지 명시해야한다. 피봇 설정과 리스트 분할하는 방법에 따라 퀵 정렬을 여러 방식으로 나눈다.

  • 호어 분할 Hoare Partition
    리스트에서 첫번째 데이터를 피봇으로 정하는 방식. 이후에 피봇보다 큰 데이터를 왼쪽 끝에서부터 선택하고, 피봇보다 작은 데이터를 오른쪽 끝에서 선택한 다음 두 데이터의 위치를 서로 변경한다. 그 다음 다시 피봇보다 큰 데이터와 작은 데이터를 찾는데, 왼쪽에서부터 찾는 값과 오른쪽에서부터 찾는 값의 위치가 서로 엇갈릴 경우 작은 데이터와 피봇의 위치를 변경한다. 피봇 위치가 이동된 상태에서 왼쪽 리스트는 피봇보다 작고, 오른쪽 리스트는 피봇보다 크다는 특징이 있다.

피봇의 왼쪽에 피봇보다 작은 데이터가, 오른쪽은 큰 데이터가 위치하게 하는 작업을 분할 Divide 또는 파티션 Partition이라고 한다.

이 상태에서 왼쪽과 오른쪽 리스트를 피봇을 설정해 동일한 방식으로 정렬을 수행하여, 리스트의 전체 요소에 정렬이 이루어지도록 한다.

array = [3, 7, 1, 0, 11, 2, 6, 4, 5, 8, 9]

def quick_sort(array, start, end) : 
    if start >= end : 
        return 
    pivot = start
    left = start + 1
    right = end
    while left <= right : 
        while left <= end and array[left] <= array[pivot] : 
            left += 1
        while right > start and array[right] >= array[pivot] : 
            right -= 1
        if left > right: #엇갈릴 경우 
            array[right], array[pivot] = array[pivot], array[right]
        else :
            array[left], array[right] = array[right], array[left]
    quick_sort(array, start, right-1)
    quick_sort(array, right+1, end)

quick_sort(array, 0, len(array)-1)
  • 퀵 정렬의 시간 복잡도
    퀵 정렬의 평균 시간복잡도는 O(NlogN)이다. 선택정렬과 삽입 정렬의 시간 복잡도는 O(N^2)으로 두 정렬 알고리즘에 비해 빠른 편이다.
    퀵 정렬의 최선의 경우에서 피봇값의 위치가 변경되어 분할이 될 때마다 정확히 왼쪽 리스트와 오른쪽 리스트를 절반씩 분할한다면 데이터의 갯수가 N개일 때 높이는 약 logN으로 판단할 수 있다. 다만 퀵 정렬의 평균적인 시간복잡도는 O(NlogN)이지만 최악의 경우 시간 복잡도는 O(N^2)가 된다. 데이터가 무작위순일 경우 퀵 정렬은 빠르게 동작할 확률이 높지만, 호어분할 방식과 같이 가장 왼쪽 요소를 피봇으로 삼을 때, '이미 데이터가 정렬된 경우' 매우 느리게 동작한다.

@heydoy
Copy link
Member Author

heydoy commented Jul 9, 2022

계수정렬

계수정렬 Count Sort알고리즘은 특정 조건이 부합할 때만 사용할 수 있으나 매우 빠른 정렬 알고리즘이다. 예를 들어 모든 데이터가 양의 정수일 경우, 데이터의 개수가 N개, 최대값이 K이면 최악의 경우에도 수행시간은 O(N+K)가 된다.

이와 같이 계수 정렬은 데이터 크기범위가 제한되고 정수형태로 표현할 수 있을 때 사용할 수 있다. 일반적으로 가장 큰 데이터와 작은 데이터의 차가 1백만을 넘지 않을 때 효과적�이다.

계수 정렬은 선택, 삽입, 퀵 정렬 알고리즘처럼 데이터 값을 비교해서 위치 변경을 하는 비교기반의 정렬알고리즘이 아니라, 별도의 리스트를 선언하여 그 안에 정렬에 대한 정보를 담는다. 예를 들어 정렬할 데이터의 범위가 0~7이라면 0부터 7까지 저장하는 크기가 8인 리스트를 선언한 후, 데이터를 확인해 데이터 값과 동일한 인덱스 데이터를 1씩 증가시키면 계수정렬이 된다.

array = [7, 6, 2, 1, 0, 3, 5, 4, 2, 1, 3]
count_list = [0]*(max(array)+1)

for i in range(len(array)) :  
    count[array[i]] += 1

for i in range(len(count)): # 리스트에 있는 정렬정보 확인
    for j in range(count[i]):
        print(i, end=' ')

- 계수정렬의 시간 복잡도 
모든 데이터가 양의 정수일  데이터 개수가 N, 최대값이 K 이면 계수정렬의 시간 복잡도는 O(N+K)이다. 현존하는 정렬 알고리즘 중에서 **기수 정렬 Radix Sort** 함께 가장 빠르다고 여겨진다. 

- 계수정렬의 공간복잡도 
계수 정렬의 공간 복잡도는 O(N+K)이다. 계수정렬은 데이터가 0 999,999  두개만 존재하더라도, 리스트의 크기는 100만개가 되도록 선언해야 하는 비효율성을 초래할  있다. 동일한 값을 가진 데이터가 다수 등장할  적합한 정렬방식이다. 

예를 들어 성적 데이터의 경우 중복된 점수가 있을 수도 있어서 계수정렬이 효과적이며,  정렬은 일반적인 경우에 빠르게 동작하기 때문에 데이터 특성을 파악하기 어려우면  정렬을 이용하는 것이 권장된다. 

@heydoy
Copy link
Member Author

heydoy commented Jul 10, 2022

파이썬의 정렬 라이브러리

파이썬의 기본 정렬 라이브러리. 퀵 정렬과 동작방식이 유사한 병합정렬 Merge Sort기반으로 만들어져있다. 병합 정렬은 일반적으로는 퀵정렬보다는 느리지만 최악의 경우에도 시간 복잡도 O(NlogN)을 보장한다. 정확히는 병합정렬과 삽입정렬의 개념을 더한 하이브리드 방식으로 알고리즘을 사용하고 있다.

sorted()

리스트, 딕셔너리 자료형을 입력받아 �정렬된 리스트 형태로 반환한다.

array = [3, 7, 1, 0, 2, 8, 9]
print(sorted(array))

sort()

리스트의 원소를 바로 정렬하는 함수로 리스트 객체의 내장함수이다. 별도의 정렬된 리스트가 반환되지는 않는다.

array = [3, 7, 1, 0, 2, 8, 9]
array.sort()
print(array)

key 매개변수

sorted(), sort()를 이용할 때 key 매개변수를 입력받을 수 있다. 매개변수로 들어가는 값은 정렬의 기준이 되며, 함수가 들어가야 한다. 예를 들어 튜플로 구성된 리스트의 경우 튜플의 두번째 원소를 기준으로 삼고 싶다면 아래와 같은 형태로 작성할 수 있다.

def sort_by(items):
    return items[2]

array = [('코숏': 3), ('스핑크스':2), ('아메숏': 1)]

print(sorted(array, key=sort_by)

출력결과는 [('아메숏': 1), ('스핑크스':2), ('코숏': 3)]이 된다.

  • 정렬 라이브러리의 시간복잡도
    항상 최악의 경우에도 시간 복잡도는 O(NlogN)을 보장한다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant