Javatpoint Logo
Javatpoint Logo

The Fastest Sorting Algorithm in C++

Sorting is a common operation in computer programming, and choosing the right sorting algorithm can significantly impact the efficiency of your program. In C++, there are several sorting algorithms to choose from, each with its strengths and weaknesses. Among these algorithms, Quick Sort stands out as one of the fastest and most efficient sorting methods.

In this guide, we will delve into Quick Sort, exploring its implementation in C++, understanding its time complexity, and considering when and how to use it. By the end of this article, you'll have a clear understanding of why Quick Sort is often considered the fastest sorting algorithm in C++.

Understanding Quick Sort

Quick Sort is a highly efficient, comparison-based sorting algorithm that employs a divide-and-conquer strategy to sort an array or list of elements. It was developed by Tony Hoare in 1960 and is known for its excellent average-case performance. The basic idea behind Quick Sort is to select a "pivot" element from the array and partition the other elements into two sub-arrays: elements less than the pivot and elements greater than the pivot. This process is then recursively applied to the sub-arrays until the entire array is sorted.

The algorithm's efficiency is attributed to its ability to sort small sub-arrays quickly and in place, reducing the overall time complexity.

Key Characteristics of Quick Sort

Before diving into the implementation of Quick Sort in C++, let's understand the key characteristics that make it a compelling choice for sorting tasks:

  1. Average Time Complexity: Quick Sort has an average time complexity of O(n * log(n)), where "n" represents the number of elements in the array. This makes it one of the fastest sorting algorithms available, especially for large datasets.
  2. In-Place Sorting: Quick Sort is an in-place sorting algorithm, meaning it doesn't require additional memory for sorting. It operates directly on the input array, which can be advantageous when memory resources are limited.
  3. Divide and Conquer: The algorithm employs a divide-and-conquer strategy, which means it divides the sorting problem into smaller sub-problems. These sub-problems are solved independently, and the solutions are combined to achieve the final sorted array.
  4. Randomized Pivot Selection: To further improve its average-case performance, Quick Sort often uses a randomized approach to select the pivot element. This reduces the likelihood of encountering worst-case scenarios.
  5. Efficiency in Practice: Quick Sort is often faster in practice than its theoretical time complexity suggests. This is because it exhibits good cache behavior and minimizes data movement.

Implementing Quick Sort in C++

Now, let's dive into the implementation of Quick Sort in C++. We'll provide a simple example of how to sort an array of integers using the Quick Sort algorithm.

Output:

Sorted array: 5 6 7 11 12 13

Explanation:

In the code above, we have two main functions: partition and quickSort. The partition function takes the input array and rearranges the elements so that all elements less than the pivot are on the left, and all elements greater than the pivot are on the right. The quickSort function recursively applies the partitioning process to sort the sub-arrays until the entire array is sorted.

  1. Partition Function (partition):
    • The partition function takes an array of integers (std::vector<int>& arr), a lower index (low), and a higher index (high) as parameters.
    • It selects a pivot element, often chosen as the rightmost element (in this code, arr[high]).
    • It initializes an index i to low - 1.
    • A for loop iterates through the elements from index low to high - 1.
    • Inside the loop, it checks if the current element (arr[j]) is less than the pivot. If it is, it increments i and swaps the elements at indices i and j, effectively moving smaller elements to the left and larger elements to the right of the pivot.
    • Finally, it swaps the pivot element (arr[high]) with the element at index i + 1 to position the pivot in its correct place and returns i + 1, which is the index of the pivot.
  2. Quick Sort Function (quickSort):
    • The quickSort function is a recursive function that sorts the input array.
    • It takes the array, the lower index (low), and the higher index (high) as parameters.
    • It starts by checking if low is less than high. If this condition is met, there are at least two elements in the sub-array, and sorting is needed.
    • Inside the function, it calls the partition function to select a pivot and rearrange elements such that elements less than the pivot are on the left, and elements greater than the pivot are on the right.
    • Then, it recursively calls quickSort on the sub-arrays to the left and right of the pivot (i.e., quickSort(arr, low, pivot - 1) and quickSort(arr, pivot + 1, high)).
  3. Main Function:
    • In the main function, you create an example array of integers (arr) that you want to sort.
    • You call the quickSort function on this array, specifying the indices to sort the entire array (from 0 to n - 1, where n is the size of the array).
    • Finally, you print the sorted array to the standard output.

Analyzing Quick Sort's Time Complexity

Quick Sort's time complexity is a crucial factor in understanding its efficiency. Here's an analysis of the time complexity:

Best-Case Time Complexity: The best-case scenario occurs when the pivot chosen in each step divides the array into two nearly equal halves. In this case, Quick Sort has a time complexity of O(n * log(n)).

Average-Case Time Complexity: Quick Sort exhibits an average time complexity of O(n * log(n)) as well. This is true for most practical datasets.

Worst-Case Time Complexity: In the worst-case scenario, where the pivot is consistently chosen as the smallest or largest element, the time complexity can degrade to O(n^2). However, the probability of encountering such a worst-case scenario is relatively low, especially when a randomized pivot selection method is used.

In practice, Quick Sort is often faster than other sorting algorithms, even those with the same average time complexity. This is due to its good cache behavior and minimal data movement, making it a top choice for sorting large datasets efficiently.

Advantages of Quick Sort

Quick Sort offers several advantages that make it a popular choice for sorting tasks in C++ and other programming languages:

  1. Efficiency: Quick Sort's average time complexity of O(n * log(n)) makes it one of the fastest sorting algorithms, especially for large datasets.
  2. In-Place Sorting: It doesn't require additional memory for sorting, making it memory-efficient.
  3. Divide and Conquer: The divide-and-conquer approach simplifies the problem and optimizes the sorting process.
  4. Randomized Pivot: By selecting the pivot randomly, the algorithm minimizes the chances of encountering worst-case scenarios, enhancing its performance.
  5. Cache Efficiency: Quick Sort exhibits good cache behavior, reducing memory access times and improving performance.

Applications of Quick Sort:

QuickSort is a popular sorting algorithm that works by partitioning an array into two sub-arrays and recursively sorting them. It is known for its efficiency and is widely used in various applications where sorting is required. Here are some common applications of QuickSort:

  1. General Sorting: QuickSort is primarily used for sorting arrays or lists of elements. It is efficient and has an average-case time complexity of O(n log n). Therefore, it is commonly used in programming languages and libraries for sorting collections of data.
  2. Numerical Analysis: QuickSort can be applied in numerical analysis to solve problems such as finding the median, quartiles, or other statistical calculations. It allows for efficient sorting of large datasets in these scenarios.
  3. Database Systems: QuickSort can be utilized in database systems for sorting large amounts of data efficiently. Sorting is a fundamental operation in database management systems when retrieving or displaying sorted results.
  4. Implementing Other Algorithms: QuickSort is often used as a subroutine within other algorithms. For example, it can be used as a part of divide-and-conquer algorithms like MergeSort or in graph algorithms like Kruskal's algorithm for minimum spanning trees.
  5. Order Statistics: QuickSort can be employed to find the kth smallest (or largest) element in an array. By selecting a pivot element and partitioning the array, it is possible to quickly determine the kth element without sorting the entire array.
  6. File Systems: QuickSort is applicable in file systems to sort and organize files and directories based on names, sizes, or other attributes. It helps in improving search and retrieval operations within file systems.
  7. Computational Biology: QuickSort is used in various bioinformatics applications, such as DNA sequence alignment, genome assembly, or identifying similar sequences. Sorting plays a vital role in these processes, and QuickSort offers efficient solutions.
  8. Pivot Selection: The concept of selecting a pivot element in QuickSort has applications in various fields, such as data mining, clustering, and machine learning algorithms. Choosing an appropriate pivot can significantly impact the performance of these algorithms.
  9. Language Processing and Natural Language Processing (NLP): QuickSort can be utilized in language processing and NLP tasks that involve sorting words, phrases, or sentences. It is often used in applications such as text analysis, information retrieval, and machine translation.

When to Use Quick Sort

Quick Sort is a versatile sorting algorithm that can be used in various scenarios. Here are some situations where Quick Sort is particularly well-suited:

  1. General Sorting: When you need to sort a list or array of elements, Quick Sort is often a top choice due to its efficient average-case performance.
  2. Large Datasets: Quick Sort's efficiency shines when dealing with large datasets, where its O(n * log(n)) time complexity ensures fast sorting.
  3. In-Place Sorting: If you have limited memory resources and need an in-place sorting algorithm, Quick Sort is an excellent option.
  4. Sorting User-Defined Objects: Quick Sort can be used to sort user-defined objects or structs by defining custom comparison functions.
  5. Real-Time Systems: In real-time systems, where predictable performance is crucial, Quick Sort's average-case performance makes it a suitable choice.

Considerations and Variants

While Quick Sort is an excellent sorting algorithm in many cases, there are a few considerations to keep in mind:

  1. Worst-Case Scenarios: Although the worst-case time complexity of Quick Sort is relatively rare, in situations where predictability is essential, you might consider alternative sorting algorithms like Merge Sort, which guarantee a consistent time complexity.
  2. Randomized Pivot: To ensure good average-case performance, using a randomized pivot selection method is recommended. However, this can introduce some non-determinism into your algorithm, which may not be suitable for all applications.
  3. Stability: Quick Sort is not a stable sorting algorithm, meaning it doesn't maintain the relative order of equal elements. If stability is a requirement, you should opt for other sorting algorithms like Merge Sort or Tim Sort.
  4. Implementation Details: The implementation of Quick Sort can vary depending on the specific requirements of your project. You may need to adjust the partitioning strategy, choose a pivot selection method, or implement custom comparisons for user-defined types.

1. Quick Sort vs. Merge Sort

Quick Sort:

  • Average Time Complexity: O(n * log(n))
  • Best-Case Time Complexity: O(n * log(n))
  • Worst-Case Time Complexity: O(n^2) (rare, but possible)

In-Place Sorting: Yes

Stability: No (may change the relative order of equal elements)

Key Strengths: Fast average-case performance, in-place sorting, cache efficiency, and adaptability to various data types and structures.

Merge Sort:

  • Average Time Complexity: O(n * log(n))
  • Best-Case Time Complexity: O(n * log(n))
  • Worst-Case Time Complexity: O(n * log(n))

In-Place Sorting: No

Stability: Yes (maintains the relative order of equal elements)

Key Strengths: Stable sorting, consistent performance, and adaptability to large datasets.

Comparison:

Quick Sort is generally faster than Merge Sort in practice, especially for large datasets. However, Merge Sort offers a more predictable, consistent performance.

Quick Sort is an in-place sorting algorithm, which is beneficial when memory resources are limited, while Merge Sort requires additional memory.

Merge Sort is a stable sorting algorithm, meaning it preserves the order of equal elements. Quick Sort is not stable.

2. Quick Sort vs. Heap Sort

Quick Sort:

  • Average Time Complexity: O(n * log(n))
  • Best-Case Time Complexity: O(n * log(n))
  • Worst-Case Time Complexity: O(n^2) (rare, but possible)

In-Place Sorting: Yes

Stability: No

Key Strengths: Fast average-case performance, in-place sorting, cache efficiency, and adaptability to various data types and structures.

Heap Sort:

  • Average Time Complexity: O(n * log(n))
  • Best-Case Time Complexity: O(n * log(n))
  • Worst-Case Time Complexity: O(n * log(n))

In-Place Sorting: Yes

Stability: No

Key Strengths: Predictable performance, in-place sorting, no additional memory required, and suitability for sorting priority queues (via the heap data structure).

Comparison:

Quick Sort and Heap Sort have similar average and worst-case time complexities, making them relatively competitive in terms of efficiency.

Quick Sort often outperforms Heap Sort in practice due to its good cache behavior, which reduces data movement.

Heap Sort is a predictable and stable sorting algorithm, whereas Quick Sort is not stable and may have rare worst-case scenarios.

3. Quick Sort vs. Tim Sort

Quick Sort:

  • Average Time Complexity: O(n * log(n))
  • Best-Case Time Complexity: O(n * log(n))
  • Worst-Case Time Complexity: O(n^2) (rare, but possible)

In-Place Sorting: Yes

Stability: No

Key Strengths: Fast average-case performance, in-place sorting, cache efficiency, and adaptability to various data types and structures.

Tim Sort:

  • Average Time Complexity: O(n * log(n))
  • Best-Case Time Complexity: O(n)
  • Worst-Case Time Complexity: O(n * log(n))

In-Place Sorting: No

Stability: Yes

Key Strengths: Stable sorting, adaptability to real-world datasets, and a combination of Merge Sort and Insertion Sort for efficiency.

Comparison:

Tim Sort combines aspects of Merge Sort and Insertion Sort to provide a hybrid sorting algorithm with good real-world performance.

Quick Sort can be faster than Tim Sort for some scenarios, particularly when the dataset is very large or contains mostly unique elements.

Tim Sort is stable and efficient for datasets with existing order or partially sorted data, making it an excellent choice for practical applications.

In summary, Quick Sort is often favored for its average-case performance, in-place sorting, and adaptability, making it a top choice for many sorting tasks. However, the choice of sorting algorithm should be based on the specific requirements of your project, the nature of your data, and the trade-offs between factors like stability, memory usage, and predictable performance. Each of these rival sorting algorithms has its own strengths and weaknesses, making them suitable for different use cases.

The History of Quick Sort

Quick Sort is one of the most widely used sorting algorithms and has a rich history dating back to the early 1960s. It was developed by British computer scientist Tony Hoare, who initially conceived the algorithm while working on the Ferranti Mark 1 computer at the University of Cambridge.

Here is a brief overview of the history and evolution of Quick Sort:

1960: Birth of Quick Sort

Quick Sort was invented by Tony Hoare in 1960 when he was working on the early computer, Ferranti Mark 1. At the time, he was seeking an efficient method for sorting data, and he came up with the Quick Sort algorithm as a solution.

The algorithm was first introduced by Hoare in his research paper titled "Algorithm 64: Quicksort" in the Communications of the ACM (Association for Computing Machinery) journal in January 1962. This paper outlined the concept of Quick Sort and provided the initial algorithm.

1961: Hoare's First Implementation

In 1961, Tony Hoare implemented the Quick Sort algorithm for the first time. His initial implementation used recursion to divide the data into smaller sub-arrays and efficiently sort them.

1962: Published Paper

As mentioned earlier, Hoare's paper, "Algorithm 64: Quicksort," published in 1962, formally introduced Quick Sort to the computing community. This publication played a crucial role in popularizing the algorithm.

1970s: Widespread Adoption

Quick Sort gained popularity in the computing community during the 1970s. Its efficiency and simplicity made it an attractive choice for sorting tasks in both academia and industry.

1995: Introduction into C++ Standard Library

Quick Sort's significance led to its inclusion in the C++ Standard Library as one of the sorting algorithms available for developers. It has been a part of the library's standard sort function since then.

Variants and Optimizations

Over the years, several variants and optimizations of Quick Sort have been proposed to enhance its performance. These include strategies for selecting the pivot element, switching to alternative sorting algorithms in certain cases (e.g., using Insertion Sort for small sub-arrays), and adapting the algorithm to parallel and multi-core computing environments.

Quick Sort has undergone various optimizations and refinements. Different variations of the algorithm have been proposed, such as randomized Quick Sort and three-way partitioning Quick Sort, to further improve its performance and handle specific scenarios.

Today, Quick Sort is widely used in practice and is often the default choice for sorting in many programming languages and libraries. Its efficient nature, ease of implementation, and versatility make it a popular choice for a wide range of applications.

Examples:

Example 1: Quick Sort in C++ using an array of strings:

Output:

Original Array: apple banana cherry date grape fig 
Sorted Array: apple banana cherry date fig grape

Explanation:

  • Include Libraries: The code includes libraries for input/output and vectors.
  • Partition Function:

partition takes a vector of strings, arr, and two indices, low and high.

It selects the rightmost element (arr[high]) as the pivot.

It compares each element in the range [low, high-1] with the pivot.

If an element is less than or equal to the pivot, it moves it to the left side of the pivot.

After the loop, it places the pivot in its correct position, with smaller elements on the left and larger elements on the right, and returns the pivot index.

  • Quick Sort Function:

quickSort sorts a vector of strings by recursively selecting pivots and partitioning the array.

It starts with a range defined by low and high.

If low is less than high, it:

Calls partition to find a pivot index (pi) and partition the array.

Recursively applies quickSort to the left and right sub-arrays (before and after the pivot).

  • Main Function:

Initializes a vector of strings, arr, with unsorted elements.

Prints the original array.

Calls quickSort to sort the array.

  • Prints the sorted array.

In this example, Quick Sort is used to sort a vector of strings. The partition and swap operations are performed based on string comparisons. Compile and run this code to see how Quick Sort can be applied to different data types.

Example 2: Sorting an Array of Floating-Point Numbers

Output:

Original Array: 9.1 7.2 5.3 11.4 2.5 1.6
Sorted Array: 1.6 2.5 5.3 7.2 9.1 11.4

Explanation:

  • Choose a pivot element from the array.
  • Rearrange the elements so that elements less than or equal to the pivot are on one side, and elements greater than the pivot are on the other side.
  • Recursively apply Quick Sort to the sub-arrays on both sides of the pivot.
  • The recursion stops when sub-arrays have zero or one element.
  • The original array is sorted as elements are correctly positioned relative to the pivot.
  • The process is repeated for each sub-array created during partitioning.
  • Quick Sort is an efficient, comparison-based sorting algorithm.
  • Average-case time complexity is O(n log n), making it one of the fastest general-purpose sorting algorithms.
  • Pivot selection can impact performance.
  • Quick Sort sorts in-place, requiring no additional memory.
  • Worst-case time complexity is O(n^2) if pivot selection is consistently poor.
  • Frequently used for sorting large datasets.

Example 3: Sorting a Custom Object

Output:

Original Students:
Alice - 85
Bob - 72
Charlie - 94
David - 68
Eve - 91

Sorted Students (by score):
David - 68
Bob - 72
Alice - 85
Eve - 91
Charlie - 94

Explanation:

  • Student Class:

A custom class named Student is defined to represent students.

Each Student object has two attributes: name (a string) and score (an integer).

A constructor is provided to initialize these attributes when a Student object is created.

  • Comparison Function:

A custom comparison function named compareStudents is defined.

This function takes two Student objects as input and compares them based on their score attributes.

It returns true if the score of the first student is less than the score of the second student, indicating that the first student should come before the second in the sorted order.

  • Main Function:

In the main function, a vector called students is created.

This vector contains several Student objects, each representing a student with a name and a score.

  • Printing Original Students:

The program prints the original list of students to the console.

For each student in the students vector, it displays the student's name and score.

  • Sorting Students:

The std::sort function is used to sort the students vector.

It rearranges the students based on their scores in ascending order.

The compareStudents function is used as the comparison criterion to determine the order of students during sorting.

  • Printing Sorted Students:

After sorting, the program prints the list of students again to the console.

This time, it displays the students in ascending order of their scores, showing their names and scores.

In summary, this program demonstrates how to define a custom class (Student), create objects of that class, sort them based on a specific attribute (score), and display both the original and sorted lists of students. The sorting is performed using the std::sort function with a custom comparison function.

In this example, we have a custom Student class, and we use Quick Sort to sort a vector of Student objects based on their scores.

These examples demonstrate the flexibility of Quick Sort in sorting different data types and custom objects. You can adapt Quick Sort to work with various data structures by defining appropriate comparison functions or operator overloads for your specific use case.

Sorting techniques from faster to slower

Sorting algorithms have different time complexities and performance characteristics depending on the input data and the specific scenario. Here's a ranking of common sorting techniques from faster to slower, along with brief explanations:

Quick Sort:

Quick Sort is often the fastest for general sorting tasks. It has an average-case time complexity of O(n * log(n)) and is known for its excellent cache behavior and adaptability to different data types.

Merge Sort:

Merge Sort is known for its consistent performance. It has an average and worst-case time complexity of O(n * log(n)), making it efficient for sorting large datasets. It's also stable, meaning it preserves the relative order of equal elements.

Heap Sort:

Heap Sort has an average and worst-case time complexity of O(n * log(n)). It is an in-place sorting algorithm with predictable performance. It's often used in scenarios where you need a reliable, memory-efficient sorting solution.

Tim Sort:

Tim Sort is a hybrid sorting algorithm that combines elements of Merge Sort and Insertion Sort. It's designed for efficiency with real-world data and is stable. Its average-case time complexity is O(n * log(n)).

Intro Sort:

Intro Sort is a variant of Quick Sort that switches to Heap Sort when the recursion depth exceeds a certain threshold. It combines the advantages of Quick Sort's speed with Heap Sort's worst-case performance guarantee.

Bubble Sort:

Bubble Sort has a time complexity of O(n^2) in the worst and average cases, making it less efficient than the previous algorithms. It's primarily used for educational purposes due to its simplicity.

Selection Sort:

Selection Sort also has a time complexity of O(n^2) in both worst and average cases. It's straightforward but less efficient than other algorithms for large datasets.

Insertion Sort:

Insertion Sort, with a time complexity of O(n^2) in the worst and average cases, is often used for small datasets or as part of more complex algorithms like Tim Sort.

Shell Sort:

Shell Sort improves upon Insertion Sort by comparing elements that are farther apart before sorting them. It has a better time complexity than the basic quadratic algorithms but is still slower than the more advanced ones mentioned earlier.

The actual performance of these sorting algorithms can vary depending on factors such as the size of the dataset, the initial order of the data, and the hardware and software environment. The choice of sorting algorithm should be based on the specific requirements and constraints of one's project.

Conclusion

Quick Sort is often considered the fastest sorting algorithm in C++ due to its average time complexity of O(n * log(n)) and efficient in-place sorting. Its divide-and-conquer strategy, randomized pivot selection, and cache efficiency contribute to its impressive performance in practice.

When choosing a sorting algorithm in C++, consider the characteristics of your data and the specific requirements of your project. Quick Sort is an excellent choice for general sorting tasks, particularly when dealing with large datasets and memory constraints.

However, it's essential to keep in mind the potential for worst-case scenarios and the non-deterministic nature of the algorithm when using randomized pivot selection. In some cases, alternative sorting algorithms like Merge Sort or Tim Sort may be more suitable.

By understanding the strengths and considerations of Quick Sort, you can make an informed decision on whether it's the right sorting algorithm for your programming needs, ultimately optimizing the efficiency of your applications.







Youtube For Videos Join Our Youtube Channel: Join Now

Feedback


Help Others, Please Share

facebook twitter pinterest

Learn Latest Tutorials


Preparation


Trending Technologies


B.Tech / MCA