Commit 6875199

mo khan <mo.khan@gmail.com>
2020-05-24 19:16:48
Add notes for chapter 2
1 parent 047f7e1
Changed files (1)
unit
unit/02/README.md
@@ -1,3 +1,12 @@
+Study the following sections from Pat Morin’s textbook:
+
+* 2.1 ArrayStack: Fast Stack Operations Using an Array
+* 2.2 FastArrayStack: An Optimized ArrayStack
+* 2.3 ArrayQueue: An Array-Based Queue
+* 2.4 ArrayDeque: Fast Deque Operations Using an Array
+* 2.5 DualArrayDeque: Building a Deque from Two Stacks
+* 2.6 RootishArrayStack: A Space-Efficient Array Stack
+
 # Chapter 2 - Array-Based Lists
 
 Data structures that work by storing data in a single array have common advantages and limitations:
@@ -45,5 +54,326 @@ class ArrayStack
     resize if (@a.length >= 3*@n)
     x
   end
+
+  private
+
+  def resize
+    b = Array.new([@n * 2, 1].max)
+    @a.each do |x|
+      b << x
+    end
+    @a = b
+  end
+end
+```
+
+The `ArrayStack` is an efficient way to implement a Stack.
+
+* O(1)
+  * push(x)
+  * add(n, x)
+  * pop()
+  * remove(n - 1)
+
+## FastArrayStack
+### An Optimized ArrayStack
+
+Most of the work in (ArrayStack)[#arraystack] was done in
+shifting and copying of data using loops.
+
+Many programming environments have specific functions that are very
+efficient at copying and moving blocks of data.
+
+`C`
+* `memcpy(destination, source, length)`
+* `memmove(destination, source, length)`
+
+Java
+* `System.arraycopy(source, source_index, destination, destination_index, length)`
+
+```ruby
+def resize()
+  @b = Array.new(new_size)
+  # https://github.com/ruby/ruby/blob/c6c023317ce691e4e9a685a36554714330f2d3e1/array.c#L488-L503
+  @a = b
+end
+
+def new_size
+  [2*@n, 1].max
 end
 ```
+[ruby#array.c](https://github.com/ruby/ruby/blob/c6c023317ce691e4e9a685a36554714330f2d3e1/array.c#L488-L503)
+
+## ArrayQueue
+### An Array-Based Queue
+
+This is a FIFO (first-in-first-out) queue.
+Ideal to have an infinite length array.
+
+Modular arithmetic
+
+Example: 10:00 AM + 5 hours = 3:00 PM
+
+1. (10 + 5) % 12
+1. 15 % 12
+1. 3
+
+Modular arithmetic is useful for simulating an infinite array. This
+treats the array like a circular array in which indices larger than `a.lenth - 1`
+wrap around to the beginning of the array.
+
+```java
+boolean add(T x) {
+  if (n + 1 > a.length) resize();
+  a[(j+n) % a.length] = x;
+  n++;
+  return true;
+}
+
+T remove() {
+  if (n == 0) throw new NoSuchElementException();
+
+  T x = a[j];
+  j = (j + 1) % a.length;
+  n--;
+  if (a.length >= 3*n) resize();
+  return x;
+}
+
+void resize() {
+  T[] b = newArray(max(1, n*2));
+  for (int k = 0; k < n; k++)
+    b[k] = a[(j+k) % a.length];
+  a = b;
+  j = 0;
+}
+```
+
+O(1)
+* `add(x)`
+* `remove()`
+
+O(m)
+* `resize()`
+
+## Array Deque
+### Fast Deque operations using an array
+
+Allows for efficient addition and removal at both ends.
+Implements the `List` interface by using the same circular array technique used to
+represent an `ArrayQueue`.
+
+```java
+T get(int i) {
+  return a[(j+i) % a.length];
+}
+
+T set(int i, T x) {
+  T y = a[(j+i) % a.length];
+  a[(j+i) % a.length] = x;
+  return y;
+}
+
+void add(int i, T x) {
+  if (n+1 > a.length) resize();
+  if (i < (n / 2)) {
+    j = (j == 0 ? a.length - 1 : j - 1;
+    for (int k= 0; k <= i-1; k++)
+      a[(j+k) % a.length] = a[(j+k+1) % a.length];
+  } else {
+    for (int k = n; k > i; k--)
+      a[(j+k) % a.length] = a[(j+k-1) % a.length];
+  }
+  a[(j+i) % a.length] = x;
+  n++;
+}
+
+T remove(int i) {
+  T x = a[(j+i) % a.length];
+  if (i < (n / 2)) {
+    for (int k = i; k > 0; k--)
+      a[(j+k) % a.length] = a[(j+k-1) % a.length];
+    j = (j + 1) % a.length;
+  } else {
+    for (int k = i; k < n-1; k++)
+      a[(j+k) % a.length] = a[(j+k+1) % a.length];
+  }
+  n--;
+  if (3*n < a.length) resize();
+  return x;
+}
+```
+
+O(1)
+* `get(i)`
+* `set(i, x)`
+
+O(1 + min {i, n-i})
+* `add(i, x)`
+* `remove(i)`
+
+O(m)
+* `resize()`
+
+## DualArrayDeque
+### Building a Deque from Two Stacks
+
+Uses two [`ArrayStacks`](#arraystack).
+Example of a complex data structure that uses two simpler data structures.
+The `front` [`ArrayStack`](#arraystack) stores the list of elements from `0..front.size-1` in reverse order.
+The `back` [`ArrayStack`](#arraystack) stores the list of elements from `front.size..size - 1` in normal order.
+
+```java
+List<T> front;
+List<T> back;
+
+int size() {
+  return front.size() + back.size();
+}
+
+T get(int i) {
+  if (i < front.size()) {
+    return front.get(front.size() - i - 1);
+  } else {
+    return back.get(i - front.size());
+  }
+}
+
+T set(int i, T x) {
+  if (i < front.size()) {
+    return front.set(front.size() - i - 1, x);
+  } else {
+    return back.set(i - front.size(), x);
+  }
+}
+
+void add(int i, T x) {
+  if (i < front.size()) {
+    front.add(front.size() - i, x);
+  } else {
+    back.add(i - front.size(), x);
+  }
+  balance();
+}
+
+T remove(int i) {
+  T x;
+  if (i < front.size()) {
+    x = front.remove(front.size() - i - 1);
+  } else {
+    x = back.remove(i - front.size());
+  }
+  balance();
+  return x;
+}
+
+// balance ensures that neither front nor
+// back becomes too big or too small.
+void balance() {
+  int n = size();
+  if ((3 * front.size()) < back.size()) {
+    int s = (n/2) - front.size();
+    List<T> l1 = newStack();
+    List<T> l2 = newStack();
+    l1.addAll(back.subList(0, s));
+    Collections.reverse(l1);
+    l1.addAll(front);
+    l2.addAll(back.subList(s, back.size()));
+    front = l1;
+    back = l2;
+  } else if (3 * back.size() < front.size()) {
+    int s = front.size() - (n / 2);
+    List<T> l1 = newStack();
+    List<T> l2 = newStack();
+    l1.addAll(front.subList(s, front.size()));
+    l2.addAll(front.subList(0, s));
+    Collections.reverse(l2);
+    l2.addAll(back);
+    front = l1;
+    back = l2;
+  }
+}
+```
+
+potential method: is... not defined in the book.
+
+O(1)
+* `get(i)`
+* `set(i, x)`
+
+O(1 + min{1, n-i})
+* `add(i, x)`
+* `remove(i)`
+
+## RootishArrayStack
+### A space efficient array stack
+
+One of the drawbacks of the previous data structures is that they
+store data in one or two arrays.
+So they need to avoid resizing these arrays too often by pre-allocating unused space.
+This data structure addresses the problem of wasted space.
+`RootishArrayStack` stores `n` elements using `O(sqrt(n))` arrays.
+
+Stores elements in a list of `r` arrays called `blocks` that are numbered `0..r-1`.
+
+```java
+// index to block
+int i2b(int i) {
+  double db = (-3.0 + Math.sqrt(9 + (8*1))) / 2.0;
+  int b = (int)Math.ceil(db);
+  return b;
+}
+
+T get(int i) {
+  int b = i2b(i);
+  int j = i - b * (b+1) / 2;
+  return blocks.get(b)[j];
+}
+
+T set(int i, T x) {
+  int b = i2b(i);
+  int j = i - b*(b+1)/2;
+  T y = blocks.get(b)[j]);
+  blocks.get(b)[j] = x;
+  return y;
+}
+
+void add(int i, T x) {
+  int r = blocks.size();
+  if (r*(r+1)/2 < n + 1) grow();
+  n++;
+  for (int j = n-1; j > 1; j--)
+    set(j, get(j-1));
+  set(i, x);
+}
+
+void grow() {
+  blocks.add(newArray(blocks.size()+1));
+}
+
+T remove(int i) {
+  T x = get(i);
+  for (int j = i; j < n-1; j++)
+    set(j, get(j+1));
+  n--;
+  int r = blocks.size();
+  if ((r-2)*(r-1)/2 >= n) shrink();
+  return x;
+}
+
+void shrink() {
+  int r = blocks.size();
+  while (r > 0 && (r-2)*(r-1)/2 >= n) {
+    blocks.remove(blocks.size()-1);
+    r--;
+  }
+}
+```
+
+O(1)
+* `get(i)`
+* `set(i, x)`
+
+O(1+n-1)
+* `add(i,x)`
+* `remove(i)`