Quick Sort Algorithm in DSA Python - Time & Space Complexity
We want to understand how the time to sort a list using Quick Sort changes as the list gets bigger.
How does the number of steps grow when the input size increases?
Analyze the time complexity of the following code snippet.
def quick_sort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + middle + quick_sort(right)
This code sorts a list by picking a pivot, splitting the list into parts, and sorting each part recursively.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Splitting the list into smaller lists around the pivot using list comprehensions.
- How many times: This splitting happens at each recursive call, which can be up to the number of elements in the list.
Each time the list is split, the work done is proportional to the size of the current list. The total work depends on how balanced the splits are.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 50 to 100 operations |
| 100 | About 700 to 1,000 operations |
| 1000 | About 10,000 to 15,000 operations |
Pattern observation: When splits are balanced, operations grow roughly like n times log n; if splits are unbalanced, it can grow much faster.
Time Complexity: O(n log n)
This means the time to sort grows a bit faster than the size of the list, but much slower than checking every pair.
[X] Wrong: "Quick Sort always takes the same time regardless of input order."
[OK] Correct: If the pivot choices are poor, Quick Sort can take much longer, especially if the list is already sorted or nearly sorted.
Understanding Quick Sort's time complexity helps you explain why it is fast on average and what cases to watch out for, showing your grasp of sorting efficiency.
"What if we always picked the first element as the pivot? How would the time complexity change?"
Practice
Quick Sort algorithm?Solution
Step 1: Understand Quick Sort approach
Quick Sort picks a pivot element and divides the list into parts smaller and larger than the pivot.Step 2: Recursive sorting
It then sorts these parts recursively, combining them to get the sorted list.Final Answer:
Divide the list into smaller parts using a pivot and sort them recursively -> Option CQuick Check:
Quick Sort = Divide and conquer [OK]
- Confusing Quick Sort with Bubble Sort
- Thinking Quick Sort merges sorted halves
- Assuming Quick Sort inserts elements one by one
Solution
Step 1: Identify required parameters
The partition function needs the array and the current low and high indices to work on a sublist.Step 2: Match function signature
Only def partition(arr, low, high): correctly includes all three parameters: arr, low, and high.Final Answer:
def partition(arr, low, high): -> Option AQuick Check:
Partition needs array and index bounds [OK]
- Omitting low and high parameters
- Using pivot as parameter instead of indices
- Defining partition with only one parameter
def quick_sort(arr, low, high):
if low < high:
pi = partition(arr, low, high)
quick_sort(arr, low, pi - 1)
quick_sort(arr, pi + 1, high)
def partition(arr, low, high):
pivot = arr[high]
i = low - 1
for j in range(low, high):
if arr[j] <= pivot:
i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i + 1], arr[high] = arr[high], arr[i + 1]
return i + 1
arr = [3, 6, 8, 10, 1, 2, 1]
quick_sort(arr, 0, len(arr) - 1)
print(arr)Solution
Step 1: Understand partition and recursive calls
The partition function places the pivot in correct position and divides array into smaller and larger parts. Quick sort recursively sorts these parts.Step 2: Trace sorting of given array
Starting with [3,6,8,10,1,2,1], after full recursion, the array becomes sorted ascending: [1,1,2,3,6,8,10].Final Answer:
[1, 1, 2, 3, 6, 8, 10] -> Option AQuick Check:
Quick Sort output = sorted array ascending [OK]
- Thinking Quick Sort sorts descending by default
- Confusing original array with sorted output
- Ignoring pivot placement effect
def partition(arr, low, high):
pivot = arr[low]
i = low + 1
for j in range(low + 1, high):
if arr[j] < pivot:
arr[i], arr[j] = arr[j], arr[i]
i += 1
arr[low], arr[i-1] = arr[i-1], arr[low]
return i-1Solution
Step 1: Check loop range for partitioning
The for loop uses range(low + 1, high), which excludes the 'high' index element. It should include 'high' to consider the pivot's correct position.Step 2: Correct loop range
Changing to range(low + 1, high + 1) includes the last element, ensuring all elements are compared to pivot.Final Answer:
The for loop should include 'high' index by using range(low + 1, high + 1) -> Option DQuick Check:
Loop must cover full sublist including high index [OK]
- Forgetting to include last element in loop
- Changing pivot position unnecessarily
- Returning wrong index after partition
def partition(arr, low, high):
pivot = arr[high]
i = low - 1
for j in range(low, high):
if ???:
i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i + 1], arr[high] = arr[high], arr[i + 1]
return i + 1What should replace
??? to sort by string length?Solution
Step 1: Understand sorting by string length
To sort strings by length, compare lengths of elements, not the strings themselves.Step 2: Replace condition with length comparison
Usinglen(arr[j]) <= len(pivot)ensures elements with length less or equal to pivot's length go left.Final Answer:
len(arr[j]) <= len(pivot) -> Option BQuick Check:
Compare lengths to sort strings by length [OK]
- Comparing strings lexicographically instead of length
- Using >= instead of <= causing reverse order
- Using unrelated string methods like startswith
Ready to prove it?
Pass the test and this pattern moves unknown
