mirror of
https://github.com/jackyzha0/quartz.git
synced 2025-12-23 21:04:07 -06:00
auto update
This commit is contained in:
parent
909046b6d6
commit
2a2242f1a2
@ -1,8 +1,14 @@
|
||||
---
|
||||
title: "04-requirements"
|
||||
sr-due: 2022-04-06
|
||||
sr-interval: 15
|
||||
sr-ease: 232
|
||||
tags:
|
||||
- info201
|
||||
- lecture
|
||||
sr-due: 2022-04-10
|
||||
sr-interval: 3
|
||||
sr-ease: 250
|
||||
---
|
||||
|
||||
[requirements](notes/requirements.md)
|
||||
|
||||
@ -1,17 +1,24 @@
|
||||
---
|
||||
title: "07-mergesort-1"
|
||||
sr-due: 2022-04-26
|
||||
sr-interval: 23
|
||||
sr-ease: 250
|
||||
tags:
|
||||
- cosc201
|
||||
- lecture
|
||||
---
|
||||
|
||||
# Divide and conquer
|
||||
[mergeosrt](notes/mergeosrt.md)
|
||||
|
||||
#unfinished
|
||||
|
||||
# 1 Divide and conquer
|
||||
|
||||
1. pre ⇒ break apartinto two or more smaller problems whose size add up to at most n
|
||||
2. Rec ⇒ solve those problems recursively
|
||||
3. post ⇒ combine solutions into a solution of the original problem
|
||||
|
||||
## 1 quicksort
|
||||
## 1.1 quicksort
|
||||
|
||||
pre ⇒ select pivot and split the array
|
||||
|
||||
@ -26,79 +33,4 @@ works best of primitive types as they can be stored in the fastest memory locati
|
||||
- memory access can be localised and the comparisions are direct
|
||||
- those advantages are limited when sorting objects of reference type
|
||||
- i that case each element of the array is just a reference to where the object really is
|
||||
- so there are no local access advantages
|
||||
|
||||
# Mergesort
|
||||
|
||||
a variant of a divide and conquer sorting array
|
||||
|
||||
pre ⇒ split array into two pieces of nearly equal size,
|
||||
|
||||
rec ⇒ sort the pieces,
|
||||
|
||||
post ⇒ merge the pieces
|
||||
|
||||
## 2 Merge
|
||||
|
||||
take the two lowest values
|
||||
|
||||
place the lowest of the two in the next place in the sorted array
|
||||
|
||||
## 3 Implementation
|
||||
|
||||
- given: a and b are sorted arrays. m in an array whose sixe is the sum fo their sizes
|
||||
- desired outcome: the elements of a and b have been copoed into m in sorted order
|
||||
|
||||
- maiain indices, ai, bi, and mi of the active location in a b and m
|
||||
- if both ai and bi represent actual indices of a and b, find the one which points to the lesser value (break ties in favour of a) copy that vale into m at mi and increment mi and whichever of ai or bi was used for the copy.
|
||||
- once one of ai and bi is out of range, copy the rest of the other array into the remainder of m
|
||||
|
||||
```java
|
||||
public static int[] merge (int[] a int[] b){
|
||||
int[] m = new int[a.length + b.length]
|
||||
int ai = 0, bi = 0, mi = 0;
|
||||
|
||||
while(ai < a.length && bi < b.length) {
|
||||
if(a[ai] <= b[bi]) m[mi++] = a[ai++];
|
||||
else m[mi++] = b[bi++]
|
||||
}
|
||||
|
||||
while (ai < a.length) m[mi++] = a[ai++];
|
||||
while (bi < b.length) m[mi++] = a[bi++];
|
||||
|
||||
return m;
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public static void mergeSort(int[] a){
|
||||
mergeSort(a, 0, a.length);
|
||||
}
|
||||
|
||||
public static void mergeSort(int[] a, int lo, int hi){
|
||||
if(hi - lo <= 1) return;
|
||||
int mid = (hi + lo)/2;
|
||||
mergeSort(a, lo, mid);
|
||||
mergeSort(a, mid, hi);
|
||||
merge(a, lo, mid, hi);
|
||||
}
|
||||
|
||||
public static void merge(int[] a, int lo, int mid, int hi){
|
||||
int[] t = new int [hi-lo];
|
||||
//adjust code from 'merge' here so that the part of a from lo to mid, and the part of a from mid to hi are merged into t
|
||||
System.arraycopy(t, 0, a, lo, hi-lo) //copy back into a
|
||||
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
## 4 Complexity
|
||||
|
||||
- n is the length of a plus the length of b
|
||||
- no obvious counter controlled loop
|
||||
- key ⇒ in each of the three loops mi in incremented by one.
|
||||
|
||||
- ∴ the total number of loop bodies executed is always n
|
||||
- since each loop has a constant amount of work
|
||||
- ∴ so total cost is **ϴ(n)**
|
||||
- so there are no local access advantages
|
||||
@ -1,110 +1,11 @@
|
||||
---
|
||||
title: "08-mergesort-2"
|
||||
sr-due: 2022-04-06
|
||||
sr-interval: 8
|
||||
sr-ease: 270
|
||||
tags:
|
||||
- cosc201
|
||||
- lecture
|
||||
---
|
||||
|
||||
recall definition of merge sort
|
||||
- pre ⇒ split
|
||||
- rec ⇒ sort pieces
|
||||
- post ⇒ merge
|
||||
|
||||
## 1 Complexity
|
||||
|
||||
no counters
|
||||
|
||||
pre and post pahses are constant and ϴ(n)
|
||||
|
||||
so M(n) = ϴ(n) + 2 * M(n/2)
|
||||
|
||||
does this even help. what if n is odd
|
||||
|
||||
pretend ϴ(n) is $C \times n$
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
M(n) &= C \times n+2 \times M(n/2) \\
|
||||
&= C \times n+2 \times (C \times (n/2) + 2 \times M(n/4))\\
|
||||
&= C \times (2n) + 4 \times M(n/4) \\
|
||||
&= C \times (2n) + 4 \times (C \times (n/4)) + 2 \times M(n/8))\\
|
||||
&= C \times (3n) + 8 \times M(n/8)\\ \\
|
||||
&= C \times (kn) + 2^k \times M(n/2^k)
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
ends when we find base case
|
||||
when we get to $n/2^k = 1$
|
||||
we could split earlier.
|
||||
the work done base case is (bounded by) some constatn D
|
||||
so if $k$ is large enough that $n/2^k$ is a base case, we get
|
||||
|
||||
$$
|
||||
M(n) = C \times (kn) + 2^k \times D
|
||||
$$
|
||||
|
||||
how big is $k$
|
||||
|
||||
$k <=lg(n)$
|
||||
|
||||
so:
|
||||
$$
|
||||
M(n) ≤ C \times (n lg(n)) + D(n) = ϴ(n lg(n))
|
||||
$$
|
||||
|
||||
which is true
|
||||
|
||||
> In a divide and consiwer algo wher pre and pst processign work are Ο(n) and the division is into parts of size at least n for some contatn c > 0 tge total time complexity is Ο(n lg n) and generally ϴ(n log n)
|
||||
|
||||
## 2 Variations of mergesort
|
||||
|
||||
unite and conquer
|
||||
|
||||
5 | 8 | 2 | 3 | 4 | 1 | 7 | 6
|
||||
|
||||
5 8 | 2 3 | 1 4 | 6 7
|
||||
|
||||
2 3 5 8 | 1 4 6 7
|
||||
|
||||
1 2 3 4 5 6 7 8
|
||||
|
||||
```java
|
||||
public static void mergeSort(int[] a) {
|
||||
int blockSize = 1;
|
||||
while(blockSize < a.length) {
|
||||
int lo = 0;
|
||||
while (lo + blockSize < a.length) {
|
||||
int hi = lo + 2*blockSize;
|
||||
if (hi > a.length) hi = a.length;
|
||||
merge(a, lo, lo + blockSize, hi);
|
||||
lo = hi;
|
||||
}
|
||||
blockSize *=2;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
outer loop is executed lg n times, where n is the length of a
|
||||
|
||||
inner loop proceeds until we find a block that "runs out of elements"
|
||||
|
||||
inner loop is having 2 x blocksize added each time, to runs most n/2 x blocksize
|
||||
|
||||
inside inner is call to merge which is ϴ(blocksize)
|
||||
|
||||
|
||||
### 2.1 complexity from bottom up
|
||||
|
||||
- $n$ is the numbe of elemetns in a
|
||||
- outer loop is executed
|
||||
|
||||
![[Pasted image 20220329114859.png#invert]]
|
||||
|
||||
### 2.2 improvments
|
||||
some arrays have sections that are already sorted
|
||||
|
||||
you canm
|
||||
|
||||
### 2.3 timsort
|
||||
used by python java rust etc
|
||||
[mergeosrt](notes/mergeosrt.md)
|
||||
|
||||
@ -6,7 +6,7 @@ tags:
|
||||
---
|
||||
links: [[notes/cosc-201]]
|
||||
|
||||
- [[notes/07-mergesort-1]]
|
||||
- [[notes/08-mergesort-2]]
|
||||
- [[notes/09-stacks-and-queues]]
|
||||
- [[notes/10-heaps-and-heapsort]]
|
||||
- [07-mergesort-1](notes/07-mergesort-1.md)
|
||||
- [08-mergesort-2](notes/08-mergesort-2.md)
|
||||
- [09-stacks-and-queues](notes/09-stacks-and-queues.md)
|
||||
- [10-heaps-and-heapsort](notes/10-heaps-and-heapsort.md)
|
||||
156
content/notes/mergeosrt.md
Normal file
156
content/notes/mergeosrt.md
Normal file
@ -0,0 +1,156 @@
|
||||
---
|
||||
title: "mergesort"
|
||||
tags:
|
||||
- cosc201
|
||||
- algorithm
|
||||
---
|
||||
|
||||
Mergesort is a [divide-and-conquer](notes/divide-and-conquer.md) algorithm. It works by recursively splitting the array in half then merging the two (sorted) halfs together . It has three main steps. These are:
|
||||
|
||||
- pre-processing: split the array into two pieces
|
||||
- recurive step: sort each of the pieces
|
||||
- post-processing: merge the two pieces
|
||||
|
||||
e.g.,
|
||||
|
||||
7 5 3 9 1 8 2 5 4 0
|
||||
|
||||
7 5 3 9 1 | 8 2 5 4 0
|
||||
|
||||
7 5 | 3 9 1 | 8 2 5 | 4 0
|
||||
|
||||
5 7 | 1 9 3 | 2 8 5 | 0 4
|
||||
|
||||
1 3 5 7 9 | 0 2 4 5 8
|
||||
|
||||
0 1 2 3 4 4 6 7 8 9
|
||||
|
||||
# 1 Implementation
|
||||
|
||||
## 1.1 Merge
|
||||
|
||||
Given: a and b are sorted arrays. m is an array whose size is the sum of their sizes
|
||||
Get: a sorted array containing the elements of a and b
|
||||
|
||||
Keep track of indices, ai, bi, and mi of the active location of a, b, and m.
|
||||
Find which of ai or bi is lesser (break ties is favour of a), and copy that value into m at mi, and increment mi and whichever of ai or bi was used.
|
||||
Once ai or bi is out of range, copy the rest of the other array into the remainder of m
|
||||
|
||||
```java
|
||||
public static int[] merge (int[] a int[] b){
|
||||
int[] m = new int[a.length + b.length]
|
||||
int ai = 0, bi = 0, mi = 0;
|
||||
|
||||
while(ai < a.length && bi < b.length) {
|
||||
if(a[ai] <= b[bi]) m[mi++] = a[ai++];
|
||||
else m[mi++] = b[bi++]
|
||||
}
|
||||
|
||||
while (ai < a.length) m[mi++] = a[ai++];
|
||||
while (bi < b.length) m[mi++] = a[bi++];
|
||||
|
||||
return m;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 1.1.1 Complexity of merge
|
||||
|
||||
$\theta(n)$
|
||||
|
||||
$n$ is the sum of the lengths of $a$ and $b$
|
||||
|
||||
Each time we loop the parameter $mi$ must increase by one, and this parameter runs from $0$ to $n-1$
|
||||
|
||||
Since the total number of loop bodies executed is $n$ and each loop does a constant amount of work, the total time complexity is $\theta(n)$
|
||||
|
||||
|
||||
## 1.2 Sort
|
||||
|
||||
```java
|
||||
public static void mergeSort(int[] a) {
|
||||
int blockSize = 1;
|
||||
while (blockSize < a.length) {
|
||||
int lo = 0;
|
||||
while (lo + blockSize < a.length) {
|
||||
int hi = lo + 2*blockSize;
|
||||
if (hi > a.length) hi = a.length;
|
||||
merge(a, lo, lo + blockSize, hi);
|
||||
lo = hi;
|
||||
}
|
||||
blockSize *= 2;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 1.2.1 Complexity of Sort
|
||||
|
||||
$\theta(n\ lg\ n)$
|
||||
|
||||
> In a divide and conquer algorithm where pre and pst processing work are Ο(n) and the division is into parts of size at least n for some contatn c > 0 the total time complexity is Ο(n lg n) and generally ϴ(n log n)
|
||||
|
||||
#### 1.2.1.1 Top down
|
||||
|
||||
We can split into the three steps to analyse this.
|
||||
|
||||
- Pre has constant time i.e., $\theta(1)$
|
||||
- Post has linear time i.e., $\theta(n)$
|
||||
|
||||
Therefore: $M(n) = \theta(n) + 2 \times M(n/2))$
|
||||
|
||||
Substitue $\theta(n)$ with $C \times n$ then
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
M(n) &= C \times n+2 \times M(n/2) \\
|
||||
&= C \times n+2 \times (C \times (n/2) + 2 \times M(n/4))\\
|
||||
&= C \times (2n) + 4 \times M(n/4) \\
|
||||
&= C \times (2n) + 4 \times (C \times (n/4)) + 2 \times M(n/8))\\
|
||||
&= C \times (3n) + 8 \times M(n/8)\\ \\
|
||||
&= C \times (kn) + 2^k \times M(n/2^k)
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
This stops (at least) when we reach the base case of $n/2^k=1$ . We could stop earlier and
|
||||
|
||||
If we do a constant amount of work $D$ when we reach the base case we get:
|
||||
|
||||
$$
|
||||
M(n) = C \times (kn) + 2^k \times D
|
||||
$$
|
||||
|
||||
where $k\leq lg(n)$ so:
|
||||
|
||||
$$
|
||||
M(n) ≤ C \times (n lg(n)) + D(n) = ϴ(n\ lg(n))
|
||||
$$
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### 1.2.1.2 Bottom Up
|
||||
|
||||
- Let n be the number of elements in the array, $a$.
|
||||
- The outer while loop (controlled by blockSize, or $b$) is executed $lg(n)$ times since its upper bound is n and b is doubled each time.
|
||||
- In the inner loop, the update on lo is to add $2b$ so it is executed $n/(2b)$ times.
|
||||
- The inner loop merges two arrays of size b, so each instance does $\theta(b)$ work.
|
||||
- That gives an upper bound on the work done in one instance of the outer loop of the form:
|
||||
|
||||
$$
|
||||
(n/(2b)) \times (A \times b) = (A/2) \times n
|
||||
$$
|
||||
|
||||
and a matching lower bound.
|
||||
|
||||
|
||||
- Thus, the work done in one instance of the outer loop is $\theta(n)$
|
||||
- And so, the total complexity is $\theta(n\ lg\ n)$.
|
||||
|
||||
The bottom-up version does exactly the same thing as the top-down version, just in an apparently different order, so this analysis applies to the top-down version as well.
|
||||
|
||||
|
||||
# 2 Variations of Mergesort
|
||||
|
||||
[[unite and conquer]] #unfinished
|
||||
56
content/notes/unite-and-conquer.md
Normal file
56
content/notes/unite-and-conquer.md
Normal file
@ -0,0 +1,56 @@
|
||||
---
|
||||
title: "unite-and-conquer"
|
||||
tags:
|
||||
- cosc201
|
||||
---
|
||||
|
||||
unite and conquer
|
||||
|
||||
5 | 8 | 2 | 3 | 4 | 1 | 7 | 6
|
||||
|
||||
5 8 | 2 3 | 1 4 | 6 7
|
||||
|
||||
2 3 5 8 | 1 4 6 7
|
||||
|
||||
1 2 3 4 5 6 7 8
|
||||
|
||||
```java
|
||||
public static void mergeSort(int[] a) {
|
||||
int blockSize = 1;
|
||||
while(blockSize < a.length) {
|
||||
int lo = 0;
|
||||
while (lo + blockSize < a.length) {
|
||||
int hi = lo + 2*blockSize;
|
||||
if (hi > a.length) hi = a.length;
|
||||
merge(a, lo, lo + blockSize, hi);
|
||||
lo = hi;
|
||||
}
|
||||
blockSize *=2;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
outer loop is executed lg n times, where n is the length of a
|
||||
|
||||
inner loop proceeds until we find a block that "runs out of elements"
|
||||
|
||||
inner loop is having 2 x blocksize added each time, to runs most n/2 x blocksize
|
||||
|
||||
inside inner is call to merge which is ϴ(blocksize)
|
||||
|
||||
|
||||
### 0.1.1 complexity from bottom up
|
||||
|
||||
- $n$ is the numbe of elemetns in a
|
||||
- outer loop is executed
|
||||
|
||||
![[Pasted image 20220329114859.png#invert]]
|
||||
|
||||
### 0.1.2 improvments
|
||||
some arrays have sections that are already sorted
|
||||
|
||||
you canm
|
||||
|
||||
### 0.1.3 timsort
|
||||
used by python java rust etc
|
||||
Loading…
Reference in New Issue
Block a user