1+
2+
3+
4+ """
5+ Sorting comparison demo in Python.
6+ We benchmark several classic sorting algorithms on the same random data
7+ and print how long each one takes. Comments are intentionally informal
8+ to make the code easier to read.
9+ """
10+
11+ import random
12+ import copy
13+ import time
14+
15+ #
16+ # Bubble Sort: This is mainly used for teaching purposes and is almost never used in real-world code.
17+ # It's very simple and easy to understand, but extremely slow for large datasets.
18+ # Only use this if you want to demonstrate how sorting works step by step.
19+ # It works by repeatedly swapping adjacent out-of-order elements until the whole list is sorted.
20+ def bubble_sort (arr ):
21+ n = len (arr )
22+ # After each pass, the largest element "bubbles" to the end.
23+ for i in range (n ):
24+ for j in range (0 , n - i - 1 ):
25+ # If current item is bigger than the next one, swap them.
26+ if arr [j ] > arr [j + 1 ]:
27+ arr [j ], arr [j + 1 ] = arr [j + 1 ], arr [j ]
28+
29+ #
30+ # Merge Sort: Use this when you need stable sorting and guaranteed O(n log n) performance, even in the worst case.
31+ # It's great for sorting large datasets, can be parallelized, and works well for external sorting (e.g., sorting data on disk).
32+ # Merge sort splits the list, sorts each half, then merges them back in order.
33+ def merge_sort (arr ):
34+ if len (arr ) > 1 :
35+ mid = len (arr ) // 2
36+ L = arr [:mid ]
37+ R = arr [mid :]
38+ # Recursively sort the left and right halves.
39+ merge_sort (L )
40+ merge_sort (R )
41+ i = j = k = 0
42+ # Merge the two sorted halves back into arr.
43+ while i < len (L ) and j < len (R ):
44+ if L [i ] < R [j ]: # Take the smaller head element first
45+ arr [k ] = L [i ]
46+ i += 1
47+ else :
48+ arr [k ] = R [j ]
49+ j += 1
50+ k += 1
51+ # Copy any leftovers from L
52+ while i < len (L ):
53+ arr [k ] = L [i ]
54+ i += 1
55+ k += 1
56+ # Copy any leftovers from R
57+ while j < len (R ):
58+ arr [k ] = R [j ]
59+ j += 1
60+ k += 1
61+
62+ #
63+ # Quick Sort (in-place version): This is usually the fastest sorting algorithm on average for in-memory arrays.
64+ # This version sorts the array in place, which is more memory efficient and closer to how real-world quicksort is implemented.
65+ # It uses the Lomuto partition scheme.
66+ def partition (arr , low , high ):
67+ # Lomuto partition: pick the last element as pivot
68+ pivot = arr [high ]
69+ i = low - 1
70+ for j in range (low , high ):
71+ if arr [j ] < pivot :
72+ i += 1
73+ arr [i ], arr [j ] = arr [j ], arr [i ]
74+ arr [i + 1 ], arr [high ] = arr [high ], arr [i + 1 ]
75+ return i + 1
76+
77+ def quick_sort_inplace (arr , low , high ):
78+ # Conversational: This is the in-place quick sort version, more memory efficient, and closer to what real-world libraries use.
79+ if low < high :
80+ pi = partition (arr , low , high )
81+ # Recursively sort elements before and after partition
82+ quick_sort_inplace (arr , low , pi - 1 )
83+ quick_sort_inplace (arr , pi + 1 , high )
84+
85+ #
86+ # Insertion Sort: This is a great choice for very small arrays or arrays that are already nearly sorted.
87+ # It's simple and has low overhead, so it's often used as a base case in hybrid sorting algorithms.
88+ # It works by taking the next item and inserting it into the sorted left side.
89+ def insertion_sort (arr ):
90+ for i in range (1 , len (arr )):
91+ key = arr [i ]
92+ j = i - 1
93+ # Shift larger items on the left to the right
94+ while j >= 0 and arr [j ] > key :
95+ arr [j + 1 ] = arr [j ]
96+ j -= 1
97+ # Place the key in its correct spot
98+ arr [j + 1 ] = key
99+
100+ #
101+ # Heap Sort: Use this when you need guaranteed O(n log n) performance and want to sort in place with minimal extra memory.
102+ # It's good when memory is tight, but it's usually a bit slower than quick sort or merge sort in practice.
103+ # Heap sort builds a max-heap, then repeatedly moves the max element to the end.
104+ def heapify (arr , n , i ):
105+ # Find the largest among root, left child, and right child
106+ largest = i
107+ l = 2 * i + 1 # Left child index
108+ r = 2 * i + 2 # Right child index
109+ if l < n and arr [l ] > arr [largest ]:
110+ largest = l
111+ if r < n and arr [r ] > arr [largest ]:
112+ largest = r
113+ # If the largest isn't the parent, swap and continue heapifying
114+ if largest != i :
115+ arr [i ], arr [largest ] = arr [largest ], arr [i ]
116+ heapify (arr , n , largest )
117+
118+ def heap_sort (arr ):
119+ n = len (arr )
120+ # Build a max-heap (rearrange array)
121+ for i in range (n // 2 - 1 , - 1 , - 1 ):
122+ heapify (arr , n , i )
123+ # Extract elements one by one, moving max to the end
124+ for i in range (n - 1 , 0 , - 1 ):
125+ arr [0 ], arr [i ] = arr [i ], arr [0 ]
126+ heapify (arr , i , 0 )
127+
128+ if __name__ == "__main__" :
129+ # Generate the same random data for all algorithms so it's a fair race.
130+ n = 20000
131+ data = [random .randint (0 , 1000000 ) for _ in range (n )]
132+
133+ # Use deep copies so each algorithm gets identical input.
134+ arr1 = copy .deepcopy (data )
135+ arr2 = copy .deepcopy (data )
136+ arr3 = copy .deepcopy (data )
137+ arr4 = copy .deepcopy (data )
138+ arr5 = copy .deepcopy (data )
139+
140+ # Time bubble sort
141+ start = time .time ()
142+ bubble_sort (arr1 )
143+ end = time .time ()
144+ # Convert seconds to milliseconds for easier reading.
145+ print ("Bubble Sort time:" , (end - start ) * 1000 , "ms" )
146+
147+ # Time merge sort
148+ start = time .time ()
149+ merge_sort (arr2 )
150+ end = time .time ()
151+ print ("Merge Sort time:" , (end - start ) * 1000 , "ms" )
152+
153+ # Time quick sort (in-place version)
154+ start = time .time ()
155+ quick_sort_inplace (arr3 , 0 , len (arr3 ) - 1 )
156+ end = time .time ()
157+ print ("Quick Sort time:" , (end - start ) * 1000 , "ms" )
158+
159+ # Time insertion sort
160+ start = time .time ()
161+ insertion_sort (arr4 )
162+ end = time .time ()
163+ print ("Insertion Sort time:" , (end - start ) * 1000 , "ms" )
164+
165+ # Time heap sort
166+ start = time .time ()
167+ heap_sort (arr5 )
168+ end = time .time ()
169+ print ("Heap Sort time:" , (end - start ) * 1000 , "ms" )
0 commit comments