|
|
CIS 4930 Top 10 Algorithms
Chris Lacher
QuickSort
|
Resources
Basic Algorithm
template <typename T>
size_t Partition2 (T* v, size_t beg, size_t end, size_t& comps)
// C++ implementation of Cormen version
{
size_t last = end - 1;
/* // randomized version: choose pivot index at random and swap into last element
size_t pivotIndex = ran(beg,end);
T pivot = v[pivotIndex];
v[pivotIndex] = v[last];
v[last] = pivot;
// */
T pivot = v[last];
size_t p = beg;
for (size_t j = beg; j != last; ++j)
{
if (!(pivot < v[j])) // if (v[j] <= pivot)
{
XC(v[p],v[j]);
++p;
}
++comps;
}
XC (v[p],v[last]);
return p;
}
template <typename T>
void QuickSort2 (T* v, size_t beg, size_t end, size_t& comps)
{
if (beg != end && beg != end - 1)
{
size_t p = Partition2(v, beg, end, comps);
QuickSort2(v, beg, p, comps);
QuickSort2(v, ++p, end, comps);
}
}
Properties
- Worst case runtime O(n2)
- Average case runtime O(n log n)
- Stack size?
Variations
- Array v. List
- Stability?
- Duplicate items - use "pivot list"?
- Randomized versions (choose partition initializer randomly; median of three)
- Ensure runtime stack size never excedes log n. (Make recursive call on
smaller part and eliminate tail recursion on the other call.)
- Eliminate recursive calls on small ranges by call to another sort. (Which
one? Choices are HeapSort, MergeSort, InsertionSort, SelectionSort. Argue your choice.)
Order Statistics
- Partition returns an index p such that all elements to the left of
p are less than or equal to v[p] and all elements to the right of
p are greater than or equal to v[p]. Note that if p
happened to be size/2 then v[p] would be the median value of
the array. However, if not, then we can get closer to a median value by another
call to Partition. The result is an algorithm for finding the median. This
generalizes to find the "kth order statistic".
- The "kth order statistic" of a set of numbers is a value s
such that k numbers in the set are less than or equal to s and
n-k numbers are greater than or equal to s. The n/2
order statistic is often called the median. Here is sample code calculating the
kth order statistic of an array:
template <typename T>
void QuickSelect (T* v, size_t beg, size_t end, size_t k, size_t& comps)
// partitions array about v[k], that is
// v[i] <= v[k] for i < k and v[i] >= v[k] for i > k
{
if (end - beg < 1) return;
if (k >= end) return;
if (k == 0) return;
size_t p = Partition2(v, beg, end, comps);
if (p > k)
QuickSelect(v, beg, p, k, comps);
if (p < k)
QuickSelect(v, p+1, end, k, comps);
}
- Refactor QuickSelect as a non-recursive algorithm
- Discuss the relationship between these two values in the case n is
even:
- v[n/2] after a call to QuickSelect(v,0,n,n/2)
- (v[n/2 - 1] + v[n/2])/2 after a call to QuickSort(v,0,n)
(The latter is an alternate definition of median value when there are an even
number of numbers in the set.)
- Can QuickSelect be relied on, or modified, so
that v[n/2 - 1] and v[n/2] are the two middle values when
n is even?
Example Driver Program
/*
sort_spy.cpp
*/
#include <iostream>
#include <iomanip>
#include <fstream>
#include <cctype>
#include <cmath>
#include <vector.h>
#include <deque.h>
#include <list.h>
#include <genalg.h>
#include <gheap.h>
#include <gsort.h>
#include <compare.h>
#include <insert.h>
#include <xstring.h>
// column widths in table
const int c1 = 15,
c2 = 9 ,
c3 = 9 ,
c4 = 13,
c5 = 10,
c6 = 13;
template < typename T >
void XC (T& x , T& y)
{
T z = x;
x = y;
y = z;
}
template < typename T >
bool CheckSort(T* a, size_t low, size_t hih)
{
if (low >= hih) return 1;
if (low == --hih) return 1;
for ( ; low < hih; ++low)
{
if (a[low] > a[low+1])
{
std::cerr << " ** bad sort result detected at index " << low << '\n';
return 0;
}
}
// std::cout << " ** sort OK\n";
return 1;
}
template < typename T >
size_t Partition00(T* a, size_t low, size_t hih, size_t& comps);
template <typename T>
void QuickSort00 (T* v, size_t beg, size_t end, size_t& comps);
template < typename T >
size_t Partition01(T* a, size_t low, size_t hih, size_t& comps);
template <typename T>
void QuickSort01 (T* v, size_t beg, size_t end, size_t& comps);
template <typename T>
size_t Partition1 (T* v, size_t first, size_t last, size_t& comps);
template <typename T>
void QuickSort1 (T* v, size_t beg, size_t end, size_t& comps);
template <typename T>
size_t Partition2 (T* v, size_t beg, size_t end, size_t& comps);
template <typename T>
void QuickSort2 (T* v, size_t beg, size_t end, size_t& comps);
template <typename T>
void QuickSelect (T* v, size_t beg, size_t end, size_t k, size_t& comps);
template < typename T >
void SelectionSort (T* array, size_t beg, size_t end, size_t& comps);
template < typename T >
void InsertionSort (T* array, size_t beg, size_t end, size_t& comps);
int main (int argc, char* argv[])
{
if (argc < 2)
{
std::cout << " ** ERROR: Expected input file name - try again **\n";
return (EXIT_FAILURE);
}
std::ifstream ifs (argv[1]);
fsu::Vector<long> v(0), w(0);
long num;
while (ifs >> num)
v.PushBack(num);
ifs.close();
size_t size = v.Size();
w.SetSize(size);
long * a = new long [size];
bool select_check, insert_check, qsort00_check, qsort01_check, qsort1_check, qsort2_check;
// SelectionSort
fsu::g_copy (v.Begin(), v.End(), a);
size_t sscomps = 0;
std::cout << "SelectionSort running ... " << std::flush;
SelectionSort(a, 0, size, sscomps);
select_check = CheckSort(a, 0, size);
std:: cout << '\n';
// */
// InsertionSort
fsu::g_copy (v.Begin(), v.End(), a);
size_t iscomps = 0;
std::cout << "InsertionSort running ... " << std::flush;
InsertionSort(a, 0, size, iscomps);
std:: cout << '\n';
insert_check = CheckSort(a, 0, size);
// */
// QuickSort00
fsu::g_copy (v.Begin(), v.End(), a);
size_t qs00comps = 0;
std::cout << "QuickSort00 running ... " << std::flush;
QuickSort00(a, 0, size, qs00comps);
std:: cout << '\n';
qsort00_check = CheckSort(a, 0, size);
// */
// QuickSort01
fsu::g_copy (v.Begin(), v.End(), a);
size_t qs01comps = 0;
std::cout << "QuickSort01 running ... " << std::flush;
QuickSort01(a, 0, size, qs01comps);
std:: cout << '\n';
qsort01_check = CheckSort(a, 0, size);
// */
// QuickSort1
fsu::g_copy (v.Begin(), v.End(), a);
size_t qs1comps = 0;
std::cout << "QuickSort1 running ... " << std::flush;
QuickSort1(a, 0, size, qs1comps);
std:: cout << '\n';
qsort1_check = CheckSort(a, 0, size);
// */
// QuickSort2
fsu::g_copy (v.Begin(), v.End(), a);
size_t qs2comps = 0;
std::cout << "QuickSort2 running ... " << std::flush;
QuickSort2(a, 0, size, qs2comps);
std:: cout << '\n';
qsort2_check = CheckSort(a, 0, size);
fsu::g_copy (a, a+size, w.Begin());
// */
// QuickMedian
fsu::g_copy (v.Begin(), v.End(), a);
size_t qmcomps = 0;
std::cout << "QuickSelect running ... " << std::flush;
QuickSelect(a, 0, size, size/2, qmcomps);
std:: cout << '\n';
// */
std::ofstream ofs;
if (argc > 2)
{
ofs.open(argv[2]);
w.Display(ofs, ' ');
ofs << '\n';
ofs.close();
}
else
{
w.Display(std::cout, ' ');
std::cout << '\n';
}
// output comps data
size_t nlogn = size*(size_t)log2(size);
size_t nsquared = size*(1+size)/2;
std::cout
<< '\n'
<< std::setw(c1) << "sort-alg" << std::setw(c2) << "check" << std::setw(c3) << "n" << std::setw(c4) << "comps" << std::setw(c5) << "n log n" << std::setw(c6) << "n(n+1)/2" << '\n'
<< std::setw(c1) << "--------" << std::setw(c2) << "-----" << std::setw(c3) << "----" << std::setw(c4) << "-----" << std::setw(c5) << "-------" << std::setw(c6) << "--------" << '\n'
<< std::setw(c1) << "Selection" << std::setw(c2) << select_check << std::setw(c3) << size << std::setw(c4) << sscomps << std::setw(c5) << nlogn << std::setw(c6) << nsquared << '\n'
<< std::setw(c1) << "Insertion" << std::setw(c2) << insert_check << std::setw(c3) << size << std::setw(c4) << iscomps << std::setw(c5) << nlogn << std::setw(c6) << nsquared << '\n'
<< std::setw(c1) << "Quick00" << std::setw(c2) << qsort00_check << std::setw(c3) << size << std::setw(c4) << qs00comps << std::setw(c5) << nlogn << std::setw(c6) << nsquared << '\n'
<< std::setw(c1) << "Quick01" << std::setw(c2) << qsort01_check << std::setw(c3) << size << std::setw(c4) << qs01comps << std::setw(c5) << nlogn << std::setw(c6) << nsquared << '\n'
<< std::setw(c1) << "Quick1" << std::setw(c2) << qsort1_check << std::setw(c3) << size << std::setw(c4) << qs1comps << std::setw(c5) << nlogn << std::setw(c6) << nsquared << '\n'
<< std::setw(c1) << "Quick2" << std::setw(c2) << qsort2_check << std::setw(c3) << size << std::setw(c4) << qs2comps << std::setw(c5) << nlogn << std::setw(c6) << nsquared << '\n'
<< '\n';
std::cout
<< '\n'
<< std::setw(c1) << "med-alg" << std::setw(c2) << "med-val" << std::setw(c3) << "n" << std::setw(c4) << "comps" << std::setw(c5) << "n log n" << std::setw(c6) << "n(n+1)/2" << '\n'
<< std::setw(c1) << "-------" << std::setw(c2) << "-------" << std::setw(c3) << "----" << std::setw(c4) << "-----" << std::setw(c5) << "-------" << std::setw(c6) << "--------" << '\n'
<< std::setw(c1) << "QSort2" << std::setw(c2) << w[size/2] << std::setw(c3) << size << std::setw(c4) << qs2comps << std::setw(c5) << nlogn << std::setw(c6) << nsquared << '\n'
<< std::setw(c1) << "QSelect" << std::setw(c2) << a[size/2] << std::setw(c3) << size << std::setw(c4) << qmcomps << std::setw(c5) << nlogn << std::setw(c6) << nsquared << '\n'
<< '\n';
return 0;
}