Chapter 4.1-4.4 with a slightly modified heapsort

This commit is contained in:
Joseph Ferano 2023-01-14 21:21:06 +07:00
parent 3e61af6cac
commit f9361490d1

View File

@ -1,5 +1,6 @@
#+TITLE: Notes & Exercises: The Algorith Design Manual
#+AUTHOR: Joseph Ferano
#+STARTUP: overview
#+OPTIONS: ^:{}
* Chapter 1
@ -357,3 +358,224 @@ printf("Final: %s\n", str);
| Before: | My | name | is | Chris |
| After: | sirhC | si | eman | yM |
| Final: | Chris | is | name | My |
* Chapter 4
** 4.1 Applications of Sorting
Apparently sorting is a big deal. There are a lot of problems that can be solved by using a sorted
list, for example; closest pair, searching, frequency distribution, convex hulls, etc;
The fastest sorting algorithms are ~n log n~, here is how it scales compared to an algorithm with
quadratic growth. Even crazier is that this table divides it by 4 so it's not that ridiculous.
| n | n^{2}/4 | n lg n |
|---------+---------------+-----------|
| 10 | 25 | 33 |
| 100 | 2,500 | 664 |
| 1,000 | 250,000 | 9,965 |
| 10,000 | 25,000,000 | 132,877 |
| 100,000 | 2,500,000,000 | 1,660,960 |
Whenever you have an algorithmic problem, don't be afraid to use sorting, because if it results in
the ability to then do a linear scan, at worst is becomes ~2n log n~, which in the end is just ~n log n~
** 4.2 Pragmatics of Sorting
There are some important things to consider when sorting; the order, keys and their data, equality,
and non-numerical data. For resolving these, we would use /comparison functions/. Here is the
signature of the C function for quicksort
#+begin_src C :include stdlib.h
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))
#+end_src
It takes a comparison function that might look like this;
#+begin_src C :include stdlib.h
int compare(int *i, int *j) {
if (*i > *j) return 1;
if (*i < *j) return -1;
return 0;
}
#+end_src
** 4.3 Heapsort
An O(n^{2}) sorting algorithm like Selection Sort can be made faster by using the right data structure,
in this case either a heap or a balanced binary tree. The first initial construction will take
one O(n), but subsequent operations within the loop will now take O(log n) rather than O(n), giving
a final complexity of O(n log n).
*** Heaps
They can either be min or max heaps and the root node will dominate its children in min-ness or
max-ness. It's also different in that it can be implemented in an array and still be reasonably
conservative with it's space complexity. However it should be noted that searching isn't efficient
because the nodes aren't guaranteed to be ordered, only the relationship between the parent/child.
Here's the full heap implementation;
#+begin_src C :includes stdio.h stdlib.h
#define PQ_SIZE 256
#define item_type int
struct priority_queue {
item_type *q;
int len;
};
struct priority_queue *pq_create() {
item_type *q = calloc(PQ_SIZE, sizeof(item_type));
struct priority_queue *pq = malloc(sizeof(struct priority_queue));
pq->q = q;
pq->len = 0;
}
int pq_parent(int n) {
if (n == 1) return -1;
else return ((int) n/2);
}
int pq_young_child(int n) {
return 2 * n;
}
// I mean technically we shouldn't need to provide the parent, since we can
// just call that ourselves
void pq_swap(struct priority_queue *pq, int n, int parent) {
item_type item = pq->q[n];
pq->q[n] = pq->q[parent];
pq->q[parent] = item;
}
void pq_bubble_up(struct priority_queue *pq, int n) {
int pidx = pq_parent(n);
if (pidx == -1) {
return;
}
item_type parent = pq->q[pidx];
item_type node = pq->q[n];
if (parent > node) {
pq_swap(pq, n, pidx);
pq_bubble_up(pq, pidx);
}
}
void pq_bubble_down(struct priority_queue *pq, int n) {
int cidx = pq_young_child(n);
if (cidx > pq->len) {
return;
}
item_type child = pq->q[cidx];
item_type node = pq->q[n];
int min_idx = n;
if (cidx <= pq->len && node > child) {
min_idx = cidx;
}
if (cidx + 1 <= pq->len && pq->q[min_idx] > pq->q[cidx + 1]) {
min_idx = cidx + 1;
}
if (node > child) {
pq_swap(pq, n, min_idx);
pq_bubble_down(pq, min_idx);
}
}
void pq_insert(struct priority_queue *pq, item_type x) {
if (pq->len >= PQ_SIZE) {
printf("Error: Priority Queue Overflow");
return;
}
pq->q[++pq->len] = x;
pq_bubble_up(pq, pq->len);
}
item_type pq_pop_top(struct priority_queue *pq) {
if (pq->len == 0) {
printf("Error: No elements in Priority Qeueu");
return -1;
}
item_type top = pq->q[1];
pq->q[1] = pq->q[pq->len--];
pq_bubble_down(pq, 1);
return top;
}
struct priority_queue* pq = pq_create();
#define ELEMENTS 8
item_type arr[ELEMENTS];
for (int i = 0; i < ELEMENTS; i++) {
arr[i] = i + 1;
}
void reverse(item_type *arr) {
for (int i = 0; i < ELEMENTS / 2; i++) {
item_type temp = arr[i];
arr[i] = arr[ELEMENTS - i - 1];
arr[ELEMENTS - i - 1] = temp;
}
}
void shuffle(item_type *arr) {
for (int i = 0; i < ELEMENTS - 1; i++) {
float r = (float)rand() / RAND_MAX;
int idx = (int)(ELEMENTS * r);
item_type temp = arr[idx];
arr[idx] = arr[i];
arr[i] = temp;
}
}
void print_elems(item_type *arr) {
for (int i = 0; i < ELEMENTS; i++) {
printf("%d,", arr[i]);
if (i == ELEMENTS - 1) {
printf("\n");
}
}
}
void print_pq(struct priority_queue *pq) {
for (int i = 1; i <= pq->len; i++) {
printf("%d,", pq->q[i]);
if (i == pq->len) {
printf("\n");
}
}
}
reverse(arr);
for (int i = 0; i < ELEMENTS; i++) {
pq_insert(pq, arr[i]);
}
print_pq(pq);
pq_pop_top(pq);
print_pq(pq);
#+end_src
** 4.4 War Story
Apparently calculating airline tickets is hard
** 4.5 Mergesort
** 4.6 Quicksort
** 4.7 Distribution Sort: Bucketing
** 4.8 War Story
** 4.9 Binary Search and Related Algorithms
** 4.10 Divide-and-Conquer