Finish ArrayDeque.

This commit is contained in:
Kyle Isom 2018-02-21 09:04:08 -08:00
parent 6311eaf378
commit 6d1ad44409
3 changed files with 68 additions and 5 deletions

View File

@ -28,6 +28,32 @@ resizing isn't too bad.
* The analysis of add and remove didn't consider cost of resize. * The analysis of add and remove didn't consider cost of resize.
* An amortised analysis is done instead that considers the cost of all calls * An amortised analysis is done instead that considers the cost of all calls
to add and remove, given a sequence of *m* calls to either. to add and remove, given a sequence of *m* calls to either.
* Lemma: if an empty ArrayStack is created and any sequence of *m* >= 1 calls * **Lemma**: if an empty ArrayStack is created and any sequence of *m* >= 1
to add and remove are performed, the total time spent in calls to resize is calls to add and remove are performed, the total time spent in calls to
O(m). resize is O(m).
* Optimisations (FastArrayStack): using memcpy or std::copy to copy blocks of
data, not one element at a time.
## ArrayQueue
* ArrayStack is a bad implementation for a FIFO queue; either add or remove
must work from the head with index = 0, which means all calls to that
method will result in running time of O(n).
* We could do this with an infinite array, using an index into the head (*j*)
and the size of the backing array. We don't have an infinite array, so we'll
have to use modular arithmetic with a finite stack.
* **Theorem**: Ignoring the cost of calls to resize, an ArrayQueue supports the
operations add and remove in O(1) per operation. Beginning with an empty
ArrayQueue, any sequence of m add/remove operations will result in a total
of O(m) time resizing.
## ArrayDeque
* Implementation of adding and removing from both ends using the same circular
buffer technique.
* add/remove check whether their index is before or after the halfway point and
shift from there as a performance benefit.
* **Theorem**: Ignoring the cost of calls to resize, an ArrayDeque supports
set/get in time O(1) time per operation, and add/remove in O(1+min(i, n-1))
time per operation. Beginning with an empty ArrayDeque, performing any
sequence of m operations results in a total of O(m) time resizing.

View File

@ -1,3 +1,4 @@
#include <cassert>
#include <iostream> #include <iostream>
#include <ods/array.h> #include <ods/array.h>
#include <ods/array_stack.h> #include <ods/array_stack.h>
@ -51,16 +52,30 @@ main(void)
cout << "=== ArrayDeque ===" << endl; cout << "=== ArrayDeque ===" << endl;
ArrayDeque<int> ad(1); ArrayDeque<int> ad(1);
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
ad.add(0, i); ad.add(0, 4-i);
} }
cout << "size: " << ad.size() << ", cap: " << ad.cap() << endl; cout << "size: " << ad.size() << ", cap: " << ad.cap() << endl;
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
ad.add(ad.size() - 1, i); ad.add(ad.size(), i);
} }
cout << "size: " << ad.size() << ", cap: " << ad.cap() << endl; cout << "size: " << ad.size() << ", cap: " << ad.cap() << endl;
for (int i = 0; i < ad.size(); i++) { for (int i = 0; i < ad.size(); i++) {
cout << i << "\t" << ad.get(i) << endl; cout << i << "\t" << ad.get(i) << endl;
} }
auto x = ad.remove(1);
assert(x == 1);
x = ad.remove(7);
assert(x == 3);
cout << "size: " << ad.size() << ", cap: " << ad.cap() << endl;
for (int i = 0; i < ad.size(); i++) {
cout << i << "\t" << ad.get(i) << endl;
}
ad.remove(2);
ad.remove(3);
ad.remove(4);
cout << "size: " << ad.size() << ", cap: " << ad.cap() << endl;
} }

View File

@ -55,6 +55,28 @@ public:
n++; n++;
} }
T remove(int i) {
T x = a[index(i)];
if (i < (n/2)) {
for (int k = i; k > 0; k--) {
a[index(k)] = a[index(k-1)];
}
j = index(1);
}
else {
for (int k = i; k < n-1; k++) {
a[index(k)] = a[index(k+1)];
}
}
n--;
if (a.length > 3 * n) {
resize();
}
return x;
}
void resize(void) { void resize(void) {
Array<T> b(max(2 * n, 1)); Array<T> b(max(2 * n, 1));
for (int k = 0; k < n; k++) { for (int k = 0; k < n; k++) {