ScholarQuill logoScholarQuillUniversity Notes
  • Notes
  • Past Papers
  • Blogs
  • Todo
Login
ScholarQuill logoScholarQuillUniversity Notes
Login
NotesPast PapersBlogsTodo
More
SubjectsDiscussionCGPA CalculatorGPA CalculatorStudent PortalCourse Outline
About
About usPrivacy PolicyReportContact
Notes
Past Papers
Blogs
Todo
Analytics
    Current Subject
    🧩
    Design & Analysis of Algorithms
    DC-321
    Progress0 / 28 topics
    Topics
    1. Introduction2. Role of Algorithms in Computing3. Analysis on Nature of Input and Size of Input4. Asymptotic Notations5. Big-O Notation6. Big-Ω Notation7. Big-Θ Notation8. Little-o Notation9. Little-ω Notation10. Sorting Algorithm Analysis11. Loop Invariants12. Recursion and Recurrence Relations13. Algorithm Design Techniques14. Brute Force Approach15. Divide-and-Conquer Approach16. Merge Sort17. Quick Sort18. Greedy Approach19. Dynamic Programming20. Elements of Dynamic Programming21. Search Trees22. Heaps23. Hashing24. Graph Algorithms25. Shortest Paths26. Sparse Graphs27. String Matching28. Introduction to Complexity Classes
    DC-321›Quick Sort
    Design & Analysis of AlgorithmsTopic 17 of 28

    Quick Sort

    9 minread
    1,504words
    Intermediatelevel

    Quick Sort

    Quick Sort is a highly efficient, divide-and-conquer, comparison-based sorting algorithm. It is often used in practice because of its average time complexity of O(nlog⁡n)O(n \log n)O(nlogn), which is faster than many other algorithms, including Merge Sort, for large datasets in most cases. However, Quick Sort’s worst-case time complexity is $$ O(n^2)), which can occur if the pivot selection is not optimal. Despite this, with good pivot selection, Quick Sort is one of the fastest general-purpose sorting algorithms.


    How Quick Sort Works

    Quick Sort works by selecting a pivot element from the array and then partitioning the array around the pivot. The pivot is chosen such that elements smaller than the pivot are on the left side, and elements greater than the pivot are on the right side. This step is called partitioning. After partitioning, the pivot is in its correct position, and we then recursively apply Quick Sort to the left and right subarrays.

    Steps:

    1. Pick a Pivot: Choose an element from the array to be the pivot. This can be done in several ways (e.g., first element, last element, middle element, or random element).
    2. Partition: Rearrange the array such that elements smaller than the pivot are on the left and elements greater than the pivot are on the right.
    3. Recursively Apply Quick Sort: Apply the same process recursively to the left and right subarrays formed by partitioning.

    Quick Sort Algorithm

    Here’s how the Quick Sort algorithm works step-by-step:

    1. Choose a pivot element. This can be done in several ways: choosing the first element, the last element, the middle element, or a random element. The choice of pivot affects performance.
    2. Partitioning: Rearrange the array such that elements smaller than the pivot are on the left and elements greater than the pivot are on the right.
    3. Recursion: Apply Quick Sort recursively to the subarrays on the left and right of the pivot.

    Python Implementation of Quick Sort:

    def quick_sort(arr):
        # Base case: if the array has one or zero elements, it's already sorted
        if len(arr) <= 1:
            return arr
        
        # Choose a pivot (in this case, we take the last element as the pivot)
        pivot = arr[len(arr) - 1]
        
        # Partitioning step: elements less than the pivot go to left, greater to the right
        left = [x for x in arr[:-1] if x < pivot]  # Elements less than pivot
        right = [x for x in arr[:-1] if x >= pivot]  # Elements greater than or equal to pivot
        
        # Recursively sort left and right and combine with the pivot
        return quick_sort(left) + [pivot] + quick_sort(right)
    

    Explanation of Code:

    1. Base Case: The recursion terminates when the length of the array is 1 or 0 (already sorted).
    2. Choosing the Pivot: Here, the pivot is chosen as the last element of the array (arr[len(arr) - 1]).
    3. Partitioning: We create two lists:
      • left contains elements smaller than the pivot.
      • right contains elements greater than or equal to the pivot.
    4. Recursion: We recursively sort the left and right subarrays and then combine them with the pivot element in the middle.

    Partitioning Algorithm

    The partitioning step is the heart of Quick Sort. It rearranges the elements so that all elements smaller than the pivot are on the left side, and all elements greater than the pivot are on the right side. After partitioning, the pivot is in its correct sorted position.

    Here's a more efficient implementation of partitioning using in-place swapping:

    def quick_sort(arr):
        if len(arr) <= 1:
            return arr
        
        # Choose the last element as pivot
        pivot = arr[-1]
        
        # Partitioning step
        i = -1  # Pointer for the smaller element
        for j in range(len(arr) - 1):
            if arr[j] < pivot:
                i += 1
                arr[i], arr[j] = arr[j], arr[i]
        
        # Swap the pivot into the correct position
        arr[i + 1], arr[-1] = arr[-1], arr[i + 1]
        
        # Recursively apply to the left and right subarrays
        left_sorted = quick_sort(arr[:i + 1])
        right_sorted = quick_sort(arr[i + 2:])
        
        return left_sorted + [arr[i + 1]] + right_sorted
    

    Explanation of Partitioning:

    1. Pivot: The last element is chosen as the pivot.
    2. Partitioning: We iterate through the array, and for each element smaller than the pivot, we swap it with an element on the left.
    3. Final Swap: After all elements have been processed, we swap the pivot into its correct position by placing it between the left and right subarrays.

    Time Complexity

    Quick Sort has an average time complexity of O(nlog⁡n)O(n \log n)O(nlogn), but it depends on how the pivot is selected:

    1. Best Case: The pivot divides the array into two roughly equal halves. This occurs when the pivot is always the median element.

      • Time Complexity: O(nlog⁡n)O(n \log n)O(nlogn)
    2. Average Case: On average, Quick Sort performs well, dividing the array into roughly equal subarrays. This happens in most random or well-shuffled arrays.

      • Time Complexity: O(nlog⁡n)O(n \log n)O(nlogn)
    3. Worst Case: If the pivot is always the smallest or largest element, Quick Sort performs poorly. This happens when the array is already sorted or nearly sorted.

      • Time Complexity: O(n2)O(n^2)O(n2)

    Note: The worst-case performance can be mitigated by choosing a good pivot, such as selecting a random pivot or using the median-of-three method (taking the median of the first, middle, and last elements).


    Space Complexity

    Quick Sort operates in-place, meaning that it doesn’t require extra space proportional to the input size, aside from the recursive function call stack.

    1. Space Complexity: O(log⁡n)O(\log n)O(logn) for the recursive call stack (average case).
    2. Worst-case Space Complexity: O(n)O(n)O(n) in the case of unbalanced partitions (if the pivot selection results in poor splits).

    Choosing the Pivot

    The efficiency of Quick Sort largely depends on how we choose the pivot element. Common strategies for choosing the pivot include:

    1. First Element: The first element of the array.

      • This can be a poor choice if the array is already sorted or nearly sorted.
    2. Last Element: The last element of the array.

      • Similar to the first element, this can perform poorly with sorted arrays.
    3. Middle Element: The middle element of the array.

      • This is generally better than always choosing the first or last element, but still not optimal.
    4. Random Element: A random element is chosen as the pivot.

      • This reduces the likelihood of encountering the worst-case scenario in most cases.
    5. Median of Three: The pivot is selected as the median of the first, middle, and last elements.

      • This is a more sophisticated approach and provides a better chance of avoiding worst-case performance.

    Advantages of Quick Sort

    1. Efficient on Average: Quick Sort is generally faster than other O(nlog⁡n)O(n \log n)O(nlogn) algorithms (like Merge Sort) because it tends to have smaller constant factors.
    2. In-place Sorting: It does not require additional memory for sorting, which makes it memory-efficient (unlike Merge Sort, which requires extra space).
    3. Cache-Friendly: Because Quick Sort tends to access elements that are close to each other, it tends to be cache-efficient.

    Disadvantages of Quick Sort

    1. Worst-Case Time Complexity: In the worst case (when the pivot is poorly chosen), Quick Sort can degrade to O(n2)O(n^2)O(n2). This can happen when the array is already sorted or nearly sorted.
    2. Unstable: Quick Sort is not a stable sorting algorithm, meaning it might change the relative order of equal elements. (However, a stable version can be implemented with extra work.)
    3. Recursive Overhead: Since Quick Sort uses recursion, it can result in high stack depth in the worst case.

    Summary

    Quick Sort is a highly efficient sorting algorithm with an average time complexity of O(nlog⁡n)O(n \log n)O(nlogn). It is based on the divide-and-conquer strategy and involves partitioning an array around a pivot element. Quick Sort performs well for large datasets, especially when a good pivot is chosen, but it can suffer in terms of performance if the pivot is not selected well, leading to the worst-case time complexity of O(n2)O(n^2)O(n2). Despite this, it is widely used in practice due to its average-case efficiency, in-place nature, and cache-friendliness.

    Previous topic 16
    Merge Sort
    Next topic 18
    Greedy Approach

    Past Papers

    Open this section to load past papers

    Click on Show Past Papers to see past papers.
    On This Page
      Reading Stats
      Est. reading time9 min
      Word count1,504
      Code examples0
      DifficultyIntermediate