본문 바로가기
Computer Science/Algorithm

정렬 알고리즘 코드(python)

by 데이터현 2022. 1. 8.
import time
import random
import sys
sys.setrecursionlimit(int(1e9))

def bubblesort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n-i-1): # 매번 오른쪽부터 큰 값이 정렬됨 -> n-i-1
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
    return

def improved_bubblesort(arr):
    end_index = len(arr)-1
    while end_index > 0 :
        swap = 0
        for i in range(end_index):
            # 마지막으로 스왑 된 곳까지 정렬 되었다고 판단 가능하므로 좀 더 효율 
            if arr[i] > arr[i+1] :
                arr[i], arr[i+1] = arr[i+1], arr[i]
                swap = i
        end_index = swap
    return

def selectionsort(arr):
    # 매 루프마다 가장 작은 값을 해당하는 위치에 저장하는 식
    n = len(arr)
    for i in range(n-1):
        min_index = i
        for j in range(i+1,n):
            if arr[j] < arr[min_index]:
                min_index = j
        arr[i], arr[min_index] = arr[min_index], arr[i]
    return

def insertionsort(arr):
    # 배열에서 두번째 원소부터 왼쪽의 원소들과 비교하며 교환
    for i in range(1,len(arr)):
        for j in range(i,0,-1):
            if arr[j] < arr[j-1]:
                arr[j], arr[j-1] = arr[j-1], arr[j]
            else:
                break
    return
    
def improved_insertionsort(arr):
    # 교환 연산을 최소화 하기 위해 하나씩 시프트하고 마지막에 입력
    for end in range(1, len(arr)):
        to_insert = arr[end]
        i = end
        while i > 0 and arr[i - 1] > to_insert:
            arr[i] = arr[i - 1]
            i -= 1
        arr[i] = to_insert
    return

def quick_sort(arr,start,end):
    # 피벗을 정하고 피벗과 원소들의 값을 비교하며 교환하는 정렬 알고리즘
    if start >= end:
        return
    pivot = start
    left = start + 1
    right = end
    while left<=right:
        while left <= end and arr[pivot] > arr[left]:
            left += 1
        while right > start and arr[pivot] < arr[right]:
            right -= 1
        if left > right:
            arr[pivot], arr[right] = arr[right], arr[pivot]
        else:
            arr[left], arr[right] = arr[right], arr[left]
    quick_sort(arr, start, right-1)
    quick_sort(arr, right+1, end)

def pythonic_quick_sort(arr):
    if len(arr) <= 1:
        return arr
        
    pivot = arr[0]
    tail = arr[1:]
    
    left = [x for x in tail if x <= pivot]
    right = [x for x in tail if x > pivot]

    return pythonic_quick_sort(left) + [pivot] + pythonic_quick_sort(right)

def merge_sort(arr):
    if len(arr) < 2:
        return arr
    
    mid = len(arr)//2
    low_arr = merge_sort(arr[:mid])
    high_arr = merge_sort(arr[mid:])
    
    merge_arr = []
    l, h = 0, 0

    while l < len(low_arr) and h < len(high_arr):
        if low_arr[l] < high_arr[h]:
            merge_arr.append(low_arr[l])
            l += 1
        else:
            merge_arr.append(high_arr[h])
            h += 1
    
    merge_arr += low_arr[l:] + high_arr[h:]
    return merge_arr

def improved_merge_sort(arr):
    # 1. 주어진 배열을 쪼갤 수 있을 때 까지 쪼갬 O(logn)
    # 2. 원소 값을 비교하며 병합하며 합침
    def sort(low,high):
        if high - low < 2 :
            return
        mid = (high + low) // 2
        sort(low,mid)
        sort(mid,high)
        merge(low,mid,high)

    def merge(low,mid,high):
        # 원소값을 비교 O(n)
        temp = []
        l, h = low, mid
        while l < mid and h < high:
            if arr[l] < arr[h]:
                temp.append(arr[l])
                l += 1
            else:
                temp.append(arr[h])
                h += 1
        while l < mid:
            temp.append(arr[l])
            l += 1
        while h < high:
            temp.append(arr[h])
            h += 1
        for i in range(low,high):
            arr[i] = temp[i-low]
    return sort(0,len(arr))

def heapify(arr, index, heap_size):
    largest = index
    left, right = index*2 +1 , index*2 + 2
    if left < heap_size and arr[largest] < arr[left]:
        # left 자식노드가 heap의 범위를 넘어가지 않고, 부모노드보다 크면 변경
        largest = left
    if right < heap_size and arr[largest] < arr[right]:
        # 마찬가지로 가장 큰 값을 가진 노드인지 확인
        largest = right
    if largest != index: # 인덱스 값이 변경되었으면 -> 자식노드중에 큰 값이 있다는 것
        arr[largest], arr[index] = arr[index], arr[largest]
        heapify(arr, largest, heap_size) # 변경 되었으므로 그 아래도 힙성질을 만족하는지 확인

def heap_sort(arr):
    n = len(arr)
    # max heap 만들기
    for i in range(n//2 -1, -1, -1): # 힙 성질에 따라 맨 끝 노드의 부모 노드는 n//2 -1 이다.
        heapify(arr, i, n)
    
    for i in range(n-1, -1, -1): # 인덱스 기준이므로 n-1이 가장 끝 인덱스
        arr[0], arr[i] = arr[i], arr[0] # 루트노드와 현 상태 가장 끝 노드 값 변경
        heapify(arr, 0, i) # 변경한 루트노드 값부터 다시 heap 만족하도록

def counting_sort(arr,digit):
    n = len(arr)
    # 변경된 정렬 값을 유지할 output 리스트
    output = [0] * n
    # 0 ~ 9 까지의 자리수 수를 더할 counting
    counting = [0] * 10

    # 각 배열의 원소의 자리수에 대한 개수를 counting 리스트에 추가
    for i in range(n):
        index = int(arr[i]/digit)
        counting[index % 10] += 1
    
    # 이전 count 값을 차례로 더해주게 되면, 해당 위치까지 몇개의 원소가 있는지 확인 가능
    for i in range(1,10):
        counting[i] += counting[i-1]
    
    # 안정 정렬이므로 리스트 뒷부분 부터 차레로 위치 변경해준다.
    i = n - 1
    while i>=0 :
        index = int(arr[i]/digit)
        # ouput에서 -1 해주는 이유는 counting의 값은 실제 인덱스 값보다 하나 큼
        # ex) n = 10 이라면 counting[9] <- 가장 끝 원소 == 10 실제로 output끝 값은 output[9]
        # 이 부분은 실제로 코드를 한줄씩 디버깅 하거나 직접 적으면서 생각해야 이해 됨
        output[counting[index % 10] - 1] = arr[i]
        counting[index % 10] -= 1
        i -= 1
    # 정렬된 리스트를 실제 리스트와 값 변경
    for i in range(n):
        arr[i] = output[i]

def radix_sort(arr):
    # 배열의 최대 값을 기준으로 자리수 비교
    maxValue = max(arr)
    # 일의 자리부터
    digit = 1
    while int(maxValue/digit) > 0 :
        #counting sort
        counting_sort(arr,digit)
        digit *= 10

def count_sort(arr):
    # 최대값까지 저장 할 수 있게 리스트 초기화
    counting = [0] * (max(arr)+1)
    sorted_arr = []
    
    for i in arr:
        counting[i] +=1
    # 해당 인덱스가 카운트 된 만큼 배열에 추가
    for i in range(len(counting)):
        sorted_arr += [i]*counting[i]
    # 원래 배열에 다시 정렬
    for i in range(len(arr)):
        arr[i] = sorted_arr[i]

num_list = list()
for i in range(10000):
    num_list.append(i)
start = time.time()
insertionsort(num_list)
end = time.time()
print(f"best case insertion sort {end - start:.8f} sec")

start = time.time()
improved_insertionsort(num_list)
end = time.time()
print(f"best case improved insertion sort {end - start:.8f} sec")

random.shuffle(num_list)
start = time.time()
bubblesort(num_list)
end = time.time()
print(f"bubble sort {end - start:.8f} sec")

random.shuffle(num_list)
start = time.time()
improved_bubblesort(num_list)
end = time.time()
print(f"improved bubble sort {end - start:.8f} sec")

random.shuffle(num_list)
start = time.time()
selectionsort(num_list)
end = time.time()
print(f"selection sort {end - start:.8f} sec")

random.shuffle(num_list)
start = time.time()
insertionsort(num_list)
end = time.time()
print(f"insertion sort {end - start:.8f} sec")

random.shuffle(num_list)
start = time.time()
improved_insertionsort(num_list)
end = time.time()
print(f"improved insertion sort {end - start:.8f} sec")

start = time.time()
quick_sort(num_list,0,len(num_list)-1)
end = time.time()
print(f"worst case quick_sort {end - start:.8f} sec")

random.shuffle(num_list)
start = time.time()
quick_sort(num_list,0,len(num_list)-1)
end = time.time()
print(f"quick sort {end - start:.8f} sec")

random.shuffle(num_list)
start = time.time()
pythonic_quick_sort(num_list)
end = time.time()
print(f"pythonic quick sort {end - start:.8f} sec")

random.shuffle(num_list)
start = time.time()
merge_sort(num_list)
end = time.time()
print(f"merge sort {end - start:.8f} sec")

random.shuffle(num_list)
start = time.time()
improved_merge_sort(num_list)
end = time.time()
print(f"improved merge sort {end - start:.8f} sec")

random.shuffle(num_list)
start = time.time()
heap_sort(num_list)
end = time.time()
print(f"heap sort {end - start:.8f} sec")

random.shuffle(num_list)
start = time.time()
radix_sort(num_list)
end = time.time()
print(f"radix sort {end - start:.8f} sec")


random.shuffle(num_list)
start = time.time()
count_sort(num_list)
end = time.time()
print(f"count sort {end - start:.8f} sec")

random.shuffle(num_list)
start = time.time()
num_list.sort()
end = time.time()
print(f"python implement sort {end - start:.8f} sec")

 

'Computer Science > Algorithm' 카테고리의 다른 글

#10 해시 테이블 구현  (0) 2022.01.09
#09 이진 탐색  (0) 2022.01.08
#08 파이썬 내장 정렬(Tim Sort)  (0) 2022.01.08
#08 계수 정렬(Counting Sort)  (0) 2022.01.07
#07 기수 정렬(Radix Sort)  (0) 2022.01.07

댓글