Randomized Quick Sort - for Duplicate values takes quadratic time
#include <iostream>
#include <random>
using namespace std;
int partition(int *arr, int s, int e)
{
int i = s, j = e + 1;
while (true)
{
// find item on left to swap
while (arr[++i] < arr[s]) {
if (i == e) break;
}
// find item on right to swap
while (arr[s] < arr[--j]) {
if (j == s) break;
}
// check if pointers cross
if (i >= j) break;
swap(arr[i], arr[j]);
}
swap(arr[s], arr[j]); // swap with partitioning item
return j;
}
void shuffling(int *arr, int n) {
for (int i = 0; i < n; i++) {
std::random_device rd;
std::uniform_int_distribution<int> key(0, i);
swap(arr[i], arr[key(rd)]);
}
}
void quickSort(int *arr, int s, int e) {
if (s >= e) return;
int j = partition(arr, s, e);
quickSort(arr, s, j - 1);
quickSort(arr, j + 1, e);
}
int main() {
int arr[] = {3, 2, 5, 1, 4};
shuffling(arr, 5); // shuffle needed for performance guarantee
quickSort(arr, 0, 4);
for (int i = 0; i < 5; i++)
cout << arr[i] << " ";
}
Duplicate keys: Dijkstra 3 - Way partitioning Quick Sort - handle duplicate values in o(nlgn)
v = arr[lo] = 'P'
#include <iostream>
#include <random>
using namespace std;
void shuffling(int *arr, int n)
{
for (int i = 0; i < n; i++) {
std::random_device rd;
std::uniform_int_distribution<int> key(0, i);
swap(arr[i], arr[key(rd)]);
}
}
void quickSort(int *arr, int s, int e) {
if (s >= e) return;
int it = s, gt = e, i = s;
int v = arr[s];
while (i <= gt) {
if (arr[i] < v) swap(arr[i++], arr[it++]);
else if (arr[i] > v) swap(arr[i], arr[gt--]);
else i++;
}
quickSort(arr, s, it - 1);
quickSort(arr, gt + 1, e);
}
int main() {
int arr[] = {1, 3, 3, 1, 4, 3};
shuffling(arr, 6); // shuffle needed for performance guarantee
quickSort(arr, 0, 5);
for (int i = 0; i < 6; i++)
cout << arr[i] << " ";
}
Bottom line. Randomized quicksort with 3-way partitioning reduces running time from linearithmic (nlgn) to linear in a broad class of applications.
Time Complexity
Best Case: O(nlogn)
Average Case: O(nlogn)
Worst Case: O(n2) (low probability due to randomization)
Randomization helps ensure that the algorithm performs closer to the average case most of the time, providing reliable performance in practical scenarios.
Find kth-Smallest Element using the QuickSelect Algorithm
Description: Implement a program to find the kth smallest element in an unsorted array efficiently using the QuickSelect algorithm, a quicksort algorithm variation. This algorithm allows for finding the kth smallest element in linear time complexity on average.
#include <iostream>
#include <random>
using namespace std;
int partition(int *arr, int s, int e)
{
int i = s, j = e + 1;
while (true)
{
// find item on left to swap
while (arr[++i] < arr[s])
{
if (i == e)
break;
}
// find item on right to swap
while (arr[s] < arr[--j])
{
if (j == s)
break;
}
// check if pointers cross
if (i >= j)
break;
swap(arr[i], arr[j]);
}
swap(arr[s], arr[j]); // swap with partitioning item
return j;
}
void shuffling(int *arr, int n)
{
for (int i = 0; i < n; i++)
{
std::random_device rd;
std::uniform_int_distribution<int> key(0, i);
swap(arr[i], arr[key(rd)]);
}
}
int quickSelect(int *arr, int n, int k)
{
k -= 1;
shuffling(arr, n); // shuffle needed for performance guarantee
int s = 0, e = n - 1;
while (s < e)
{
int j = partition(arr, s, e);
if (j > k)
e = j - 1;
else if (j < k)
s = j + 1;
else
return arr[k];
}
return arr[k];
}
int main()
{
int arr[] = {6, 3, 1, 6, 0, 4, 8};
int size = 7;
cout << quickSelect(arr, size, 5) << endl;
}