CISC 3130
Data Structures
Array Implementations

Overview

In this lecture, we will concentrate on independent implementations of sequences using an underlying array container.

Arrays, Sequences, and Shifting

The main challenge for achieving an efficient implementation is the shifting of elements when inserting/removing anywhere other than the end (O(n) vs O(1)).

An insertion anywhere other than the end involves a shift to the right; similarly a removal a shift to the left:

Using a Single-Index, 0-based Array

Container has first element at element 0 of the array; end of container is denoted by an index variable. As a result, the only efficient container is the stack.

Stack

Have a top-of-stack (tos) index/pointer that acts similarly to size for Vector

class ArrayStack<E> {
	void push(E value) {array[++tos] = value;}
	E pop() {
		if (isEmpty()) throw …
		return array[tos--];
	}
	boolean isEmpty() {return tos == -1;}

	private E [] array = (E)new Object[CAPACITY];
	private int tos = -1;

	private static int CAPACITY = 100;
}

Using a Resizeable Class

We might consider replacing the built-in array with the resizeable Array class we discussed, providing capacity management
class ArrayStack<E> {
	void push(E value) {array.set(++tos, value;}
	}
	E pop() {
		if (isEmpty()) throw …
		array.get(tos--];
	}
	boolean isEmpty() {return tos == -1;}

	private Array arr = new Array<E>();
	private int tos = -1;
}

Using an Array with Two-Indices

Linear Queue

The basic idea is to have two index variables: front and rear indicating first and last elements in the container. As a result, the only efficient container is the FIFO queue

FIFO Queue

To avoid shifting, have a front as well as a rear index/pointer. Again, by convention, front points at first (oldest) element, rear to last (newest).
class ArrayFifoQueue {
	void add(int value) {
		array[++rear] = value);
	}
	int remove() {
		if (isEmpty()) throw …
		return array[front++];
	}
	boolean isEmpty() {return front > rear;}

	private int [] array = new int[CAPACITY];
	private int front = 0, rear = -1;

	private static int CAPACITY = 100;
}

Circular Queues / Ring Buffers

View the array as a circular entity (again, logical vs physical) … circular/ring buffer (Wiki entry)

class ArrayFifoQueue {
	void add(int value) {
		rear++;										// can be replaced with
		if (rear >= CAPACITY) rear = 0;		// (rear = rear + 1) % CAPACITY
		array[rear] = value);
	}
	int remove() {
		int hold = array[front];
		front++;										// can be replaced with
		if (front >= CAPACITY) front = 0;	// (front = front + 1) % CAPACITY
		return hold;
	}
	boolean isEmpty() {return front > rear;}

	private int [] array = new int[CAPACITY];
	private int front = 0, rear = CAPACITY-1;

	private static int CAPACITY = 100;
}

Deque

A simple-minded, non-circular approach would be to place the deque in the center of array to avoid shifting

But now that we have the notion of a ring buffer, that is a much more efficient approach to deque as well

List

Can't avoid the shifting here for internal adds/removes

Priority Queue and the Associatives

Summary

Code Used in the Lecture