# Quick Sort Implementation in C++

Quick Sort is one of the popular sorting techniques known for its time complexity and efficiency.

## History

The Quick Sort algorithm was developed by Tony Hoare in 1959 while he was working on his computer science thesis. It is one of the most efficient and widely used sorting algorithms.

The history of Quick Sort can be traced back to the study of sorting algorithms in the early days of computer science. Tony Hoare, a British computer scientist, developed Quick Sort as a more efficient alternative to existing sorting algorithms.

Hoare initially developed the algorithm in Algol 60 programming language and published it in his thesis titled "Quicksort" in 1961. He described the algorithm as a divide-and-conquer approach that leverages the partitioning of elements to achieve efficient sorting.

Quick Sort gained popularity due to its simplicity and efficiency in practice. It has a time complexity of O(n log n) on average, making it one of the fastest sorting algorithms in most cases. The algorithm also works well with large datasets and has good cache performance.

Over the years, 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.

### Implementation of Quick Sort in C++:

Output:

```Original array:
64 25 12 22 11
Sorted array:
11 12 22 25 64
```

Explanation:

1. The swap function takes two integer pointers a and b as parameters. It swaps the values pointed to by a and b.
2. The partition function takes an array arr, the lower index low, and the higher index high as parameters. It selects the pivot element, which is typically chosen as the last element of the array (arr[high]).
3. The partition function initializes the index of the smaller element (i) to (low - 1).
4. It then iterates through the array from the low index to high - 1 using the variable j. If an element arr[j] is smaller than or equal to the pivot, it increments i by 1 and swaps arr[i] with arr[j].
5. After the loop, the pivot element is placed in its correct position by swapping it with the element at arr[i + 1]. This ensures that all elements smaller than the pivot are on the left side of it, and all elements greater than the pivot are on the right side.
6. The partition function returns the index of the pivot element.
7. The quickSort function is the main recursive function that implements the Quick Sort algorithm. It takes an array arr, the lower index low, and the higher index high as parameters.
8. In the quickSort function, if the low index is less than the high index, it means there is more than one element in the sub-array, and partitioning is required.
9. It calls the partition function to partition the array and get the index of the pivot element.
10. It then recursively calls quickSort for the sub-array before the pivot (i.e., from low to pi - 1) and the sub-array after the pivot (i.e., from pi + 1 to high).
11. The recursion continues until each sub-array contains only one element. At this point, the array is sorted.
12. The printArray function takes an array arr and the size of the array as parameters. It prints the elements of the array.
13. In the main function, an array is initialized with some values. The original array is printed using the printArray function, and then the quickSort function is called to sort the array. Finally, the sorted array is printed again using printArray.

## 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.

## 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.

1. Efficiency: QuickSort is known for its efficient average-case performance. It has an average time complexity of O(n log n), which makes it one of the fastest sorting algorithms available. In practice, QuickSort often outperforms other sorting algorithms, especially for large datasets.
2. In-place Sorting: QuickSort performs sorting in-place, which means it doesn't require additional memory proportional to the size of the input. It rearranges the elements within the original array, reducing the need for extra space and making it memory efficient.
3. Simplicity: The algorithm's concept is relatively simple and easy to understand, consisting of partitioning the array and recursively sorting the sub-arrays. The simplicity of QuickSort makes it easier to implement and debug compared to more complex sorting algorithms.
4. Good Average-case Performance: QuickSort's average-case performance is typically better than many other sorting algorithms, such as MergeSort or HeapSort. It is well-suited for situations where the input data is randomly distributed or when the input size is large.
5. Tail Recursion Optimization: QuickSort can be optimized using tail recursion, which reduces the amount of stack space required during recursive calls. This optimization helps prevent stack overflow errors and makes QuickSort more efficient for large datasets.
6. Cache Efficiency: QuickSort has good cache efficiency due to its sequential memory access pattern. It tends to exhibit good performance on modern computer architectures that utilize caching, as accessing nearby memory locations can be faster.
7. Inherent Parallelism: QuickSort can be parallelized efficiently, allowing for concurrent execution on multiple processors or threads. The partitioning step lends itself well to parallelization, making QuickSort a suitable choice for parallel computing environments.
8. Randomized Version: QuickSort can be randomized by selecting a random pivot element during partitioning. This helps avoid worst-case scenarios and ensures a more balanced partitioning, improving the average-case performance and reducing the likelihood of degenerate inputs.

These advantages make QuickSort a preferred choice in many applications where sorting is required. However, it's important to note that QuickSort does have a worst-case time complexity of O(n^2) in certain scenarios, although this is relatively rare in practice.