Python - Sorting

8 minute read

Sorting

Sorting Algorithm Best Avg Worst
Bubble Sort \(O(n)\) \(O(n^2)\) \(O(n^2)\)
Quick Sort \(O(n\log{n})\) \(O(n\log{n})\) \(O(n^2)\)
Merge Sort \(O(n\log{n})\) \(O(n\log{n})\) \(O(n\log{n})\)
Heapsort \(O(n\log{n})\) \(O(n\log{n})\) \(O(n\log{n})\)
Insertion Sort \(O(n)\) \(O(n^2)\) \(O(n^2)\)
Selection Sort \(O(n^2)\) \(O(n^2)\) \(O(n^2)\)

Bubble Sort

"""
1. adjacent elements ๋ผ๋ฆฌ ๋น„๊ตํ•œ๋‹ค. 
2. ์–ด๋–ป๊ฒŒ sorted๊ฐ€ ๋˜์—ˆ๋Š”์ง€ ํŒ๋‹จํ•˜๋Š”๊ฐ€(์ข…๋ฃŒ์กฐ๊ฑด)
    2-1. ์ฒซ pass๋ฅผ ํ†ต๊ณผํ•˜๋ฉด ๋งˆ์ง€๋ง‰ ์›์†Œ๋Š” ๋ฌด์กฐ๊ฑด ๊ฐ€์žฅ ํฐ ์›์†Œ๊ฐ€ ๋จ
    2-2. ๋”ฐ๋ผ์„œ pass ํ•  ๋•Œ๋งˆ๋‹ค ๋’ค์—์„œ๋ถ€ํ„ฐ ์ •๋ ฌ์ด ๋˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜
    2-3. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์—, pass ํ›„์— range๋Š” ๋’ค์—์„œ๋ถ€ํ„ฐ 1์”ฉ ์ค„์–ด๋“ค๊ฒŒ ํ•˜๋ฉด ๋œ๋‹ค. (์ด๊ฒŒ ํฌ์ธํŠธ โœ”)
    2-4. ๋”ฐ๋ผ์„œ ์ด์ค‘ ๋ฐ˜๋ณต๋ฌธ ์ˆ˜ํ–‰ํ›„์—๋Š” ์ข…๋ฃŒ๊ฐ€ ๋œ๋‹ค. (์›์†Œ 2๊ฐœ์”ฉ ๋น„๊ตํ•˜๊ธฐ ๋•Œ๋ฌธ์—)
    2-5. ์•ˆ์ชฝ ๋ฐ˜๋ณต๋ฌธ์€ index 1๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ range end ์ง€์ ์ด 1์”ฉ ์ค„์–ด์•ผ ํ•œ๋‹ค.
    2-6. ๋น„๊ตํ•  index๋ฅผ ํ”„๋ฆฐํŠธ ํ•ด๋ณด๋ฉด ์ด๋ ‡๊ฒŒ ๋‚˜์™€์•ผ ํ•œ๋‹ค.
    (0, 1) (1, 2) (2, 3) (3, 4)
    (0, 1) (1, 2) (2, 3) 
    (0, 1) (1, 2) 
    (0, 1)
==========
index ์„ค์ • ์‹คํŒจ
์ด์œ : i,j ๋‘๊ฐœ๋ฅผ ๋™์‹œ์— ํ•ด์•ผ๋œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.
ํ•˜๋‚˜๋งŒ ํ•ด์•ผ๋˜๋Š” ์ด์œ : ์–ด์ฐจํ”ผ adjacent์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•˜๋‚˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ +1 ํ•ด์ฃผ๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
=========
์‚ฌ์‹ค i์˜ ์šฉ๋„๋Š” j๋ฅผ ๊ตฌํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ–ˆ๋˜ ๊ฒƒ ์ด๋‹ค.
j๋ฅผ -1 ํ•ด์ฃผ๋Š” ์ด์œ ๋Š” adjacent์ด๊ธฐ ๋•Œ๋ฌธ์— j, j+1์—์„œ ์ธ๋ฑ์Šค๊ฐ€ ๋„˜์–ด๊ฐ€ ๋ฒ„๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
i๋Š” ์œ„ํ•ด์„œ ์–˜๊ธฐํ•œ ๋งจ ๋งˆ์ง€๋ง‰ ์›์†Œ๋“ค์„ ํ•˜๋‚˜์”ฉ ์ค„์ด๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ
==========
arr๋กœ ๋„˜์–ด๊ฐ€๊ธฐ ์ „์— ์˜ฌ๋ฐ”๋ฅธ index ์œ„์น˜๋ฅผ ๊ตํ™˜ํ•˜๋Š”์ง€ ํ™•์ธํ•ด ๋ณผ ๊ฒƒ.
"""
myList = list(map(int, input().split()))
# print(myList)
for i in range(len(myList)):
    for j in range(len(myList)-i-1):
        print((j, j+1))
        if myList[j] > myList[j+1]: # need swap since the former is larger.
            myList[j], myList[j+1] = myList[j+1], myList[j]
print(myList)

Quick Sort

  • Divide and Conquer
"""
๊ธฐ์ค€์ (pivot)์„ ์ •ํ•œ๋‹ค.
---------------------------------------------------------------
๊ธฐ์ค€์  ์ •ํ•˜๋Š” ๊ธฐ์ค€
- Always pick first element as pivot.  
- Always pick last element as pivot (implemented below)  
- Pick a random element as pivot.  
- Pick median as pivot.  
์•„๋งˆ random์ด ์ œ์ผ ์ข‹์ง€ ์•Š์„๊นŒ?
---------------------------------------------------------------
๊ธฐ์ค€์  ๋ณด๋‹ค ์ž‘์€ ๋ฐ์ดํ„ฐ๋Š” ์™ผ์ชฝ, ํฐ ๋ฐ์ดํ„ฐ๋Š” ์˜ค๋ฅธ์ชฝ์œผ๋กœ
์žฌ๊ท€ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉ(iterative ๋ฐฉ๋ฒ•์€?)
์ข…๋ฃŒ์กฐ๊ฑด: list์˜ ๊ธธ์ด๊ฐ€ 2๋ณด๋‹ค ์ž‘์„ ๊ฒฝ์šฐ
=======================================================
random element ๋ฅผ pivot์œผ๋กœ ํ•ด์„œ ๊ตฌํ˜„ํ•  ๊ฒฝ์šฐ
index error ๋ฐœ์ƒ
random ์œผ๋กœ ๊ตฌํ˜„ํ•  ๊ฒฝ์šฐ ์ฒซ๋ฒˆ์งธ๋‚˜ ๋งˆ์ง€๋ง‰ ์›์†Œ์™€ pivot ์›์†Œ๋ฅผ ๊ตํ™˜ํ•œ ํ›„ ๋˜‘๊ฐ™์ด ์ง„ํ–‰
=======================================================

list๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์žฌ๊ท€ํ˜ธ์ถœ์„ ํ•˜๋Š” ํ˜•ํƒœ์˜ ๊ตฌํ˜„์€ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ์ธก๋ฉด์—์„œ ๋น„ํšจ์œจ์ ์ž„
ํฐ ์‚ฌ์ด์ฆˆ์˜ ์ž…๋ ฅ์ด ๋“ค์–ด์™”์„ ๊ฒฝ์šฐ ๋‹จ์ ์ด ํฌ๊ฒŒ ๋ถ€๊ฐ๋จ
๋”ฐ๋ผ์„œ in-place ์ •๋ ฌ์ด ์„ ํ˜ธ๋˜๋Š” ์ด์œ ์ด๋‹ค.
https://www.daleseo.com/sort-quick/

----------- list quick sort --------------
def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[len(arr) // 2]
    lesser_arr, equal_arr, greater_arr = [], [], []
    for num in arr:
        if num < pivot:
            lesser_arr.append(num)
        elif num > pivot:
            greater_arr.append(num)
        else:
            equal_arr.append(num)
    return quick_sort(lesser_arr) + equal_arr + quick_sort(greater_arr)
===================================
optimized quick sort in-place implementation
https://www.geeksforgeeks.org/quick-sort/
=============================
Python QuickSort ์ตœ์ ํ™”์— ๋”ฐ๋ฅธ ์†๋„
https://choiseokwon.tistory.com/233
"""
# This Function handles sorting part of quick sort
# start and end points to first and last element of
# an array respectively
import sys
sys.setrecursionlimit(2000)
def partition(start, end, array):
    
    # Initializing pivot's index to start
    pivot_idx = start
      
    # This loop runs till start pointer crosses 
    # end pointer, and when it does we swap the
    # pivot with element on end pointer
    while start < end:          
        # Increment the start pointer till it finds an 
        # element greater than  pivot 
        while start < len(array) and array[start] <= array[pivot_idx]:
            start += 1
        # Decrement the end pointer till it finds an 
        # element less than pivot
        while array[end] > array[pivot_idx]:
            end -= 1

        # If start and end have not crossed each other, 
        # swap the numbers on start and end
        if start < end:
            array[start], array[end] = array[end], array[start]
      
    # Swap pivot element with element on end pointer.
    # This puts pivot on its correct sorted place.
    # Returning end pointer to divide the array into 2
    array[end], array[pivot_idx] = array[pivot_idx], array[end]
    return end
# The main function that implements QuickSort 
def quick_sort(start, end, array):
      
    # if start and end is not overlapped, proceed.
    if (start < end):
        # p is partitioning index, array[p] 
        # is at right place
        p = partition(start, end, array)
          
        # Sort elements before partition 
        # and after partition
        quick_sort(start, p-1, array)
        quick_sort(p+1, end, array)


N = int(input())
array = [int(input()) for _ in range(N)]
# print(array)
# print(quick_sort(array))
quick_sort(0, len(array)-1, array)
# print(array)
for e in array:
    print(e)

Merge Sort

"""
slice notation
slice ์‹œ ๋ฐฐ์—ด์˜ ๋ณต์ œ๊ฐ€ ์ผ์–ด๋‚˜๋ฏ€๋กœ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ํšจ์œจ ๋‚˜์จ
-------------------
1. ์ˆ˜๋„์ฝ”๋“œ๋ณด๊ณ  ๊ตฌํ˜„
2. ์‹คํŒจ์‹œ, ์ฝ”๋“œ๋ณด๊ณ  ์ฝ”๋ฉ˜ํŠธ ๋‹ฌ๊ณ  ์ž‘์„ฑํ•ด๋ณด๊ธฐ
3. ์žฌ๊ท€๋กœ ๊ตฌํ˜„ ์™„์„ฑ
4. iterative version ๊ตฌํ˜„ํ•˜๊ธฐ
"""
"""
MergeSort(arr[], l,  r)
If r > l
     1. Find the middle point to divide the array into two halves:  
             middle m = l+ (r-l)/2
     2. Call mergeSort for first half:   
             Call mergeSort(arr, l, m)
     3. Call mergeSort for second half:
             Call mergeSort(arr, m+1, r)
     4. Merge the two halves sorted in step 2 and 3:
             Call merge(arr, l, m, r)
# In-Place Merge Sort
<https://www.geeksforgeeks.org/in-place-merge-sort/>
# Iterative Merge Sort
<https://www.geeksforgeeks.org/iterative-merge-sort/>
"""


N = int(input())
array = [int(input()) for _ in range(N)]

# def mergeSort(arr):
#     if len(arr) < 2:
#         return arr

#     mid = len(arr) // 2
#     low_arr = mergeSort(arr[:mid])
#     high_arr = mergeSort(arr[mid:])
#     print(low_arr, high_arr, "######")

#     merged_arr = []
#     l = h = 0
#     while l < len(low_arr) and h < len(high_arr):
#         if low_arr[l] < high_arr[h]:
#             merged_arr.append(low_arr[l])
#             l += 1
#         else:
#             merged_arr.append(high_arr[h])
#             h += 1
    
#     merged_arr += low_arr[l:]
#     merged_arr += high_arr[h:]
#     print(merged_arr,' merged arr')
#     return merged_arr

def mergeSort(arr):
    if len(arr) > 1:
  
        # Finding the mid of the array
        mid = len(arr)//2
          
        # Dividing the array elements
        # into 2 halves
        L = arr[:mid]
        R = arr[mid:]
  
        # Sorting the first half
        mergeSort(L)
        # Sorting the second half
        mergeSort(R)
  
        # Copy data to temp arrays L[] and R[]
        i = j = k = 0
        while i < len(L) and j < len(R):
            if L[i] < R[j]:
                arr[k] = L[i]
                i += 1
            else:
                arr[k] = R[j]
                j += 1
            k += 1
  
        # Checking if any element was left
        while i < len(L):
            arr[k] = L[i]
            i += 1
            k += 1
        
        while j < len(R):
            arr[k] = R[j]
            j += 1
            k += 1

  
mergeSort(array)
print(array)


"""
In-place Merge Sort
-------------------
* l is for left index and r is right index of
the sub-array of arr to be sorted
"""
def merge(arr, start, mid, end):
    start2 = mid + 1
 
    # If the direct merge is already sorted
    if (arr[mid] <= arr[start2]):
        return
 
    # Two pointers to maintain start
    # of both arrays to merge
    while (start <= mid and start2 <= end):
 
        # If element 1 is in right place
        if (arr[start] <= arr[start2]):
            start += 1
        else:
            value = arr[start2]
            index = start2
 
            # Shift all the elements between element 1
            # element 2, right by 1.
            while (index != start):
                arr[index] = arr[index - 1]
                index -= 1
 
            arr[start] = value
 
            # Update all the pointers
            start += 1
            mid += 1
            start2 += 1
 
 
def mergeSort(arr, l, r):
    if (l < r):
 
        # Same as (l + r) / 2, but avoids overflow
        # for large l and r
        m = l + (r - l) // 2
 
        # Sort first and second halves
        mergeSort(arr, l, m)
        mergeSort(arr, m + 1, r)
 
        merge(arr, l, m, r)

"""
Iterative Merge Sort
"""
def merge(left, right):
    if not len(left) or not len(right):
        return left or right
 
    result = []
    i, j = 0, 0
    while (len(result) < len(left) + len(right)):
        if left[i] < right[j]:
            result.append(left[i])
            i+= 1
        else:
            result.append(right[j])
            j+= 1
        if i == len(left) or j == len(right):
            result.extend(left[i:] or right[j:])
            break

    return result

def mergesort(list):
    if len(list) < 2:
        return list
 
    middle = int(len(list)/2)
    left = mergesort(list[:middle])
    right = mergesort(list[middle:])

    return merge(left, right)

Heap Sort

"""
1. ์ˆ˜๋„์ฝ”๋“œ๋ณด๊ณ  ๊ตฌํ˜„
2. ์‹คํŒจ์‹œ, ์ฝ”๋“œ๋ณด๊ณ  ์ฝ”๋ฉ˜ํŠธ ๋‹ฌ๊ณ  ์ž‘์„ฑํ•ด๋ณด๊ธฐ
3. ์žฌ๊ท€๋กœ ๊ตฌํ˜„ ์™„์„ฑ
4. iterative version ๊ตฌํ˜„ํ•˜๊ธฐ
----------------------------------------
https://www.geeksforgeeks.org/heap-sort/
----------------------------------------
__author__ = 'Minsuk Heo'
#=======================================================================
#  Title: Heapsort
#
#  Statement:
#  Given a disordered list of integers (or any other items),
#  rearrange the integers in natural order.
#
#  Sample Input: [8,5,3,1,9,6,0,7,4,2,5]
#  Sample Output: [0,1,2,3,4,5,5,6,7,8,9]
#
#  Time Complexity of Solution:
#  Best O(nlog(n)); Average O(nlog(n)); Worst O(nlog(n)).
#
#  Approach:
#  Heap sort happens in two phases. In the first phase, the array
#  is transformed into a heap. A heap is a binary tree where
#  1) each node is greater than each of its children
#  2) the tree is perfectly balanced
#  3) all leaves are in the leftmost position available.
#  In phase two the heap is continuously reduced to a sorted array:
#  1) while the heap is not empty
#  - remove the top of the head into an array
#  - fix the heap.
#  Heap sort was invented by John Williams not by B. R. Heap.
#
#  MoveDown:
#  The movedown method checks and verifies that the structure is a heap.
#
#  Technical Details:
#  A heap is based on an array just as a hashmap is based on an
#  array. For a heap, the children of an element n are at index
#  2n+1 for the left child and 2n+2 for the right child.
#
#  The movedown function checks that an element is greater than its
#  children. If not the values of element and child are swapped. The
#  function continues to check and swap until the element is at a
#  position where it is greater than its children.
#=======================================================================
# ๋งˆ์ง€๋ง‰์— ์žˆ๋Š” ๋…ธ๋“œ๋ถ€ํ„ฐ ์ˆœํšŒํ•˜๋ฉด์„œ swapํ•˜๋ฉด ๋˜๋Š”๋ฐ, 
# leaf ๋…ธ๋“œ(์ž์‹๋…ธ๋“œ๊ฐ€ ์—†๋Š”)์˜ ๊ฒฝ์šฐ๋Š” ํ•„์š”์—†๊ธฐ ๋•Œ๋ฌธ์—,
# p์˜ ๊ฐ’์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •ํ•จ์œผ๋กœ์จ(์ด์ง„ํŠธ๋ฆฌ์˜ ํŠน์ง•์„ ํ™œ์šฉ)
# leaf ๋…ธ๋“œ์˜ parent ๋…ธ๋“œ ์ค‘์—์„œ ๋งˆ์ง€๋ง‰ index๋ถ€ํ„ฐ ์‹œ์ž‘์„ ํ•œ๋‹ค.
# p๋ฅผ ๊ตฌํ–ˆ์œผ๋‹ˆ ์ด์ œ siftdown์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
# ----------------------------------
# "siftdown"
# 1. ํ˜„์žฌ ๋…ธ๋“œ์˜ child node ์˜ ๊ฐ’์ด ํ˜„์žฌ node์˜ ๊ฐ’๋ณด๋‹ค ํฌ๋‹ค๋ฉด SWAP
# 1-1. swap ํ›„์— ๋ถ€๋ชจ ๋…ธ๋“œ์˜€๋‹ค๊ฐ€ ์ž์‹๋…ธ๋“œ๋กœ ๋ฐ”๋€ ๋…ธ๋“œ์— ๋Œ€ํ•ด ์ž์‹๋…ธ๋“œ๋ฅผ ๋‹ค์‹œ ํ™•์ธํ•˜๋Š” ๊ฒƒ์„ ๋ฐ˜๋ณต.
"""


def heapsort(a):

    
    def siftdown(a, i, size):
        l = 2*i + 1
        r = 2*i + 2
        largest = i
        if l <= size-1 and a[l] > a[i]:
            largest = l
        if r <= size-1 and a[r] > a[largest]: # ์ž์‹๋…ธ๋“œ 2๊ฐœ์ค‘ ํฐ ๊ฒƒ์„ largest ๋ณ€์ˆ˜์— ๋„ฃ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, r์—์„œ๋Š” largest์™€ r์„ ๋น„๊ต
            largest = r
        if largest != i: # largest๊ฐ€ i์™€ ๋‹ค๋ฅด๋‹ค๋ฉด, ์ฆ‰, largest๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค๋ฉด, swap์„ ํ•ด์ฃผ๊ณ  siftdown
            # SWAP
            a[i], a[largest] = a[largest], a[i]
            siftdown(a, largest, size)

    def heapify(a, size):
        
        p = (size//2)-1 
        while p>=0:
            siftdown(a, p, size)
            p -= 1

    size = len(a)
    heapify(a, size) # MAX HEAP
    end = size-1
    while(end > 0):
        # root์™€ end๋ฅผ swap ํ•ด์ค€ ํ›„
        # root๋ฅผ siftdown ํ•ด์ฃผ๋ฉด ์ •๋ ฌ์ด ๋œ๋‹ค.
        # (swap๋œ ๋…ธ๋“œ(root์—์„œ swapํ–ˆ์œผ๋ฏ€๋กœ ํ˜„์žฌ๋Š” end๋…ธ๋“œ)๋Š” siftdown์—์„œ ๋ฐฐ์ œ๋œ ์ƒํƒœ๋กœ siftdown ์ง„ํ–‰๋œ๋‹ค.)
        # (root๋Š” heapify๋œ ํ›„ ์ด๋ฏ€๋กœ ์ตœ๊ณ  ํ˜น์€ ์ตœ์†Œ์ด๊ธฐ ๋•Œ๋ฌธ์—)
        a[0], a[end] = a[end], a[0]
        siftdown(a, 0, end) # root node ๋ถ€ํ„ฐ siftdown์„ ์ง„ํ–‰
        end -= 1

arr = [1,3,2,4,9,7]
heapsort(arr)
print(arr)

Leave a comment