CS

Sorting - Six Algorithms

청구입니다 2025. 9. 3. 21:00
반응형

What is Sorting ? 

Organising data in order, either from smallest to largest or from largest to smallest. 

 

1. Bubble Sort 

Simple but slow.

[Idea] Compare adjacent numbers and push the larger one to the right.

 

def bubble_sort_example(arr):
    a = arr[:]  # keep original
    n = len(a)
    for end in range(n-1, 0, -1):
        swap_flag = False
        for i in range(end):
            if a[i] > a[i+1]: # compare
                a[i], a[i+1] = a[i+1], a[i]
                swap_flag = True
        if not swap_flag:  # early exit
            break
    return a

print(bubble_sort_example([6, 4, 3, 7, 1, 9, 8]))


'''
Step-by-step execution:
1. (6, 4) compare and swap => (4, 6, 3, 7, 1, 9, 8)
2. (6, 3) compare and swap => (4, 3, 6, 7, 1, 9, 8)
3. (6, 7) no swap
... repeat

result = [1, 3, 4, 6, 7, 8, 9]
'''

 

2. Selection Sort

 

Fewer swaps, but many comparisons.

[Idea] At each step i, find the minimun in the remaining range and swap it into position i.

 

def selection_sort_example(arr):
    a = arr[:]
    n = len(a)
    for i in range(n-1): # find the smallest element
        min_idx = i
        for j in range(i+1, n):
            if a[j] < a[min_idx]: # compare
                min_idx = j

        if min_idx != i: # swap
            a[i], a[min_idx] = a[min_idx], a[i]

    return a

print(selection_sort_example([6, 4, 3, 7, 1, 9, 8]))


'''
Step-by-step execution:
1. min_idx = 0, arr=[6, 4, 3, 7, 1, 9, 8] => min_idx = 4 => swap (6, 1) => (1, 4, 3, 7, 6, 9, 8)
2. min_idx = 1, arr=[1, 4, 3, 7, 6, 9, 8] => min_idx = 2 => swap (4, 3) => (1, 3, 4, 7, 6, 9, 8)
3. min_idx = 2, arr=[1, 3, 4, 7, 6, 9, 8] => min_idx = 2 => no swap
... repeat

result = [1, 3, 4, 6, 7, 8, 9]
'''

 

3. Insertion Sort

More efficient when the data is nearly sorted (because the cost grows with the number of inversions).

[Idea] Treat the left part as already sorted and insert the new key into its correct position.

 

def insertion_sort_example(arr):
    a = arr[:]
    for i in range(1, len(a)):
        key = a[i]
        j = i - 1
        while j >= 0 and a[j] > key:
            a[j+1] = a[j]   # move to the right
            j -= 1
        a[j+1] = key        # insert at the empty space
    return a


print(insertion_sort_example([6, 4, 3, 7, 1, 9, 8]))


'''
Step-by-step execution:
1. key = 4, j = 0, a[j] > key => a[j+1] = a[j] => (6, 6, 3, 7, 1, 9, 8) => j-1, a[j+1] = key => (6, 4, 3, 7, 1, 9, 8)
2. key = 3, j = 1, a[j] > key => a[j+1] = a[j] => (6, 4, 4, 7, 1, 9, 8) => j-1, a[j+1] = key => (6, 3, 4, 7, 1, 9, 8)
3. key = 3, j = 0, a[j] > key => a[j+1] = a[j] => (3, 6, 4, 7, 1, 9, 8) => j-1, a[j+1] = key => (3, 6, 4, 7, 1, 9, 8)
4. key = 7, j = 2, a[j] > key => a[j+1] = a[j] => (3, 6, 4, 4, 1, 9, 8) => j-1, a[j+1] = key => (3, 6, 7, 4, 1, 9, 8)
... repeat

result = [1, 3, 4, 6, 7, 8, 9]
'''

 

 

4. Merge Sort

Always O(n log n) and stable, but it use extra (auxiliary) memory.

[Idea] Split the array in half, then merge two sorted halves.

 

def merge_sort_example(arr):
    if len(arr) <= 1:
        return arr[:]

    # split
    mid = len(arr)//2 
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    
    # merge
    merged = []
    i = j = 0
    while i < len(left) and j < len(right): # start from the first element and last element
        if left[i] <= right[j]:  # compare
            merged.append(left[i]); i += 1
        else: 
            merged.append(right[j]); j += 1
    merged.extend(left[i:])
    merged.extend(right[j:])
    return merged


print(merge_sort_example([6, 4, 3, 7, 1, 9, 8]))


'''
Step-by-step execution:
1. split [6, 4, 3, 7, 1, 9, 8] => [6, 4, 3, 7], [1, 9, 8]
2. split [6, 4, 3, 7] => [6, 4], [3, 7]
3. split [6, 4] => [6], [4]
4. split [3, 7] => [3], [7]
5. merge [6, 4] => [4, 6]
6. merge [3, 7] => [3, 7]
7. merge [4, 6, 3, 7] => [3, 4, 6, 7]
... repeat

result = [1, 3, 4, 6, 7, 8, 9]
'''

 

 

5. Quick Sort

Recursively sorts the partitioned parts; generally fast.

[Idea] Select a pivot and partition : values <= pivot go left, larger values go right.

 

Steps:

1. Place the pivot at the end and gather values <= pivot to the left (with swaps)

2. Move the pivot to its final positoin; then recursively sort the left and right subarrays.

 

def quick_sort_example(arr):
    a = arr[:]

    def partition(lo, hi): # split
        pivot = a[hi]
        i = lo - 1
        for j in range(lo, hi):
            if a[j] <= pivot:
                i += 1
                a[i], a[j] = a[j], a[i]
        a[i+1], a[hi] = a[hi], a[i+1]
        return i+1

    def qs(lo, hi): # recursive
        if lo < hi:
            p = partition(lo, hi)
            qs(lo, p-1)
            qs(p+1, hi)

    qs(0, len(a)-1)
    return a


print(quick_sort_example([6, 4, 3, 7, 1, 9, 8]))


'''
Step-by-step execution:
1. split [6, 4, 3, 7, 1, 9, 8] => [6, 4, 3, 7], [1, 9, 8]
2. split [6, 4, 3, 7] => [6, 4], [3, 7]
3. split [6, 4] => [6], [4]
4. split [1, 9, 8] => [1, 9], [8]
5. split [1, 9] => [1], [9]
6. merge [6, 4] => [4, 6]
7. merge [3, 7] => [3, 7]
8. merge [1, 9] => [1, 9]
9. merge [4, 6, 3, 7, 1, 9, 8] => [1, 3, 4, 6, 7, 8, 9]

result = [1, 3, 4, 6, 7, 8, 9]
'''

 

 

6. Heap Sort 

Guarantees O(n log n) even in the worst case.

[Idea] Build a max-heap, swap the root(maximum) with the end, shrink the heap, and repeat heapify.

def heap_sort_example(arr):
    a = arr[:]
    n = len(a)

    def heapify(n, i): # make max heap
        largest = i
        l = 2*i + 1
        r = 2*i + 2

        if l < n and a[l] > a[largest]: 
            largest = l
        if r < n and a[r] > a[largest]:
            largest = r
        if largest != i:
            a[i], a[largest] = a[largest], a[i] # swap
            heapify(n, largest)

    # make max heap
    for i in range(n//2 - 1, -1, -1):
        heapify(n, i)

    # keep heap
    for end in range(n-1, 0, -1):
        a[0], a[end] = a[end], a[0]
        heapify(end, 0)

    return a



print(heap_sort_example([6, 4, 3, 7, 1, 9, 8]))


'''
Step-by-step execution:
1. heapify(7, 2) => l = 5, r = 6, largest = 2 ⇒ swap(2,5)
2. heapify(7, 5) => l = 11, r = 12, largest = 5 ⇒ [6, 4, 9, 7, 1, 3, 8]
3. heapify(7, 1) => l = 3, r = 4, largest = 1 ⇒ swap(1,3)
4. heapify(7, 3) => l = 7, r = 8, largest = 3 ⇒ [6, 7, 9, 4, 1, 3, 8]

...repeat

result = [1, 3, 4, 6, 7, 8, 9]
'''

 

 

Conclusion

Slow : Bubble, Selection, Insertion -> O(n^2)

Fast : Merge, Quick, Heap -> O(n log n)

 

 

Reference

https://en.wikipedia.org/wiki/Sorting_algorithm

https://iiibreakeriii.github.io/Post12(CSPart2_3)

https://visualgo.net/en/sorting

반응형

'CS' 카테고리의 다른 글

MIT 6.034 Study Notes — Week 1~4  (0) 2025.10.29
Graph algorithms  (0) 2025.09.21
Data Structures  (0) 2025.09.10
Complexity Analysis - Asymptotic notation & Time/Space Trade-off  (0) 2025.09.01
Complexity Analysis - Time Complexity  (0) 2025.08.29