From 6d1ad444098c1bde0e6b08e8aa60a34b3c5ce830 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Wed, 21 Feb 2018 09:04:08 -0800 Subject: [PATCH] Finish ArrayDeque. --- ods/notes/chapter2.txt | 32 +++++++++++++++++++++++++++++--- ods/src/array_test.cc | 19 +++++++++++++++++-- ods/src/ods/array_deque.h | 22 ++++++++++++++++++++++ 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/ods/notes/chapter2.txt b/ods/notes/chapter2.txt index 6152937..3f2afe0 100644 --- a/ods/notes/chapter2.txt +++ b/ods/notes/chapter2.txt @@ -28,6 +28,32 @@ resizing isn't too bad. * 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 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 - to add and remove are performed, the total time spent in calls to resize is - O(m). +* **Lemma**: if an empty ArrayStack is created and any sequence of *m* >= 1 + calls to add and remove are performed, the total time spent in calls to + 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. diff --git a/ods/src/array_test.cc b/ods/src/array_test.cc index a184dbf..bf900eb 100644 --- a/ods/src/array_test.cc +++ b/ods/src/array_test.cc @@ -1,3 +1,4 @@ +#include #include #include #include @@ -51,16 +52,30 @@ main(void) cout << "=== ArrayDeque ===" << endl; ArrayDeque ad(1); for (int i = 0; i < 5; i++) { - ad.add(0, i); + ad.add(0, 4-i); } cout << "size: " << ad.size() << ", cap: " << ad.cap() << endl; 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; for (int i = 0; i < ad.size(); i++) { 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; } diff --git a/ods/src/ods/array_deque.h b/ods/src/ods/array_deque.h index d4830fc..acd110e 100644 --- a/ods/src/ods/array_deque.h +++ b/ods/src/ods/array_deque.h @@ -55,6 +55,28 @@ public: 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) { Array b(max(2 * n, 1)); for (int k = 0; k < n; k++) {