make_heap, N * pop_heap)Divide and Conquer Strategies
Provide an Abstraction of comparison-based sorts
Key point: represent comparisons as edges on a tree. Nodes represent comparisons

What is the asymptotic height of any decision tree for sorting $N$ elements?
Theorem: Any decision tree that sorts $N$ elements has height $\Omega(N lg N)$
Thus we can claim the following:
$$ h \ge lg {\left(\frac{N}{e}\right)}^N $$
Rules of logarithms:
$$ h \ge N~lg~N - N~lg~e$$
Removing constants ($N~lg~e$)
$$\Omega(N~lg~N)$$
Idea: Don't compare the actual values. Instead, look at a single "digit" at a time
Example: $91, 06, 85, 15, 92, 35, 30, 22, 39$
int digit (int value, int place, int base = 10) {
    while (place--) {
        value /= base;
    }
    return value % base;
}
#include <vector>
using Bin = std::vector<int>;
Bin bins[10];
std::vector<int> data {91, 6, 85, 15, 92, 35, 30, 22, 39};
for (int value : data) {
    bins[digit(value , 0)].push_back (value);
}
bins
{ { 30 }, { 91 }, { 92, 22 }, {}, {}, { 85, 15, 35 }, { 6 }, {}, {}, { 39 } }
{
    auto i = data.begin();
    for (auto & bin : bins) {
        i = std::copy (bin.begin(), bin.end(), i);
        bin.clear();
    }
}
data
{ 30, 91, 92, 22, 85, 15, 35, 6, 39 }
for (int value : data) {
    bins[digit(value , 1)].push_back (value);
}
bins
{ { 6 }, { 15 }, { 22 }, { 30, 35, 39 }, {}, {}, {}, {}, { 85 }, { 91, 92 } }
{
    auto i = data.begin();
    for (auto & bin : bins) {
        i = std::copy (bin.begin(), bin.end(), i);
        bin.clear();
    }
}
data
{ 6, 15, 22, 30, 35, 39, 85, 91, 92 }
# d is the upper limit on the number of digits for a number
def radix_sort (nums, d=8):
    # create 10 empty bins
    bins = list(list() for i in range(10))
    # start at the ones digit
    div = 1
    for digit in range(d):
        # split nums into appropriate bins
        for num in nums:
            bins[(num // div) % 10].append(num)
        # copy from bins back to nums
        i = 0
        for b in range(10):
            for n in bins[b]:
                nums[i] = n
                i += 1
            bins[b].clear()
        # update to "next" digit
        div *= 10
for digit in range(d):
   ...
   for num in nums:
      ...
   for b in range(10):
       for n in bins[b]:
          ...d and Nd << N (d substantially smaller than N)Radix Sort seemed nice -- but what if we had a bunch of duplicate values?
function countingSort(array, k) is
  count ← new array of k zeros
  for i = 1 to length(array) do
    count[array[i]] ← count[array[i]] + 1
  for i = 2 to k do
    count[i] ← count[i] + count[i - 1]
  for i = length(array) downto 1 do
    output[count[array[i]]] ← array[i]
    count[array[i]] ← count[array[i]] - 1
  return outputk¶Given any set of numbers:
[min, max]max - min + 1idx = value - minTakeaways:
k can be initialized to sizevalue - min instead of value#include <algorithm>
#include <numeric>
constexpr int N = 10;
int A[N] = {4, 1, 2, 3, 4, 3, 1, 2, 0, 4};
// int [min, max] = *std::minmax_element(A, A + N);
constexpr int min = 0, max = 4;
constexpr int k = max - min + 1;
A
{ 4, 1, 2, 3, 4, 3, 1, 2, 0, 4 }
vector)int C[k]; // counting array
std::fill_n(C, k, 0);
for (int value : A) {
    ++C[value - min];
}
C
{ 1, 2, 2, 2, 3 }
// convert counts to index (via partial_sum)
std::partial_sum(C, C + k, C);
C
{ 1, 3, 5, 7, 10 }
int B[N]; // output
for (int i = N - 1; i >= 0; --i) {
    B[--C[A[i] - min]] = A[i];
}
B
{ 0, 1, 1, 2, 2, 3, 3, 4, 4, 4 }
std::copy_n (B, N, A);
A
{ 0, 1, 1, 2, 2, 3, 3, 4, 4, 4 }