Data Structures, Abstract Data Types, and Algorithm Analysis
Fundamental Data Structures: Arrays & Object References
An array is a sequenced, contiguous collection of variables all sharing the exact same data type.
Each slot or cell in an array is identified by a unique integer position known as an index, which begins at index and extends to .
The values stored within array cells are referred to as elements.
Array Capacity: The maximum number of elements an array can hold, determined at the time of allocation and remaining fixed throughout its lifecycle.
In Java, the length of an array named
ais accessed via the propertya.length.
Array Declaration and Instantiation
Literal Initialization Syntax:
Operator Instantiation Syntax:
Memory for array cells is allocated contiguously in heap memory.
Case Study: Game Entries & High Score Board
A game high-score collection maintains top scores sorted in descending numerical order.

GameEntry Class Implementation
public class GameEntry {
private String name; // name of person earning this score
private int score; // the score value
/** Constructs a game entry with given parameters. */
public GameEntry(String n, int s) {
name = n;
score = s;
}
/** Returns the name field. */
public String getName() { return name; }
/** Returns the score field. */
public int getScore() { return score; }
/** Returns a string representation of this entry. */
public String toString() {
return "(" + name + ", " + score + ")";
}
}
Scoreboard Class Implementation
public class Scoreboard {
private int numEntries = 0; // number of actual entries
private GameEntry[] board; // array of game entries (names & scores)
/** Constructs an empty scoreboard with given capacity. */
public Scoreboard(int capacity) {
board = new GameEntry[capacity];
}
/** Attempt to add a new score to the collection (if it is high enough) */
public void add(GameEntry e) {
int newScore = e.getScore();
// is the new entry e really a high score?
if (numEntries < board.length || newScore > board[numEntries - 1].getScore()) {
if (numEntries < board.length) {
numEntries++; // no score drops from the board, so overall number increases
}
// shift any lower scores rightward to make room for the new entry
int j = numEntries - 1;
while (j > 0 && board[j - 1].getScore() < newScore) {
board[j] = board[j - 1]; // shift entry from j-1 to j
j--; // and decrement j
}
board[j] = e; // when done, add new entry
}
}
/** Remove and return the high score at index i. */
public GameEntry remove(int i) throws IndexOutOfBoundsException {
if (i < 0 || i >= numEntries) {
throw new IndexOutOfBoundsException("Invalid index: " + i);
}
GameEntry temp = board[i]; // save the object to be removed
for (int j = i; j < numEntries - 1; j++) {
board[j] = board[j + 1]; // move one cell to the left
}
board[numEntries - 1] = null; // null out the old last score
numEntries--;
return temp; // return the removed object
}
}


Object References, Memory Allocation, and Aliasing
A reference variable holds the memory address location where an object resides, rather than storing the object data inline.
Default Reference Values:
Uninitialized object reference variables defined as instance or class data fields default to
null.Local reference variables declared within methods possess no default value and must be explicitly assigned prior to use.
Trace of Reference Manipulations:

Step (a):
Integer p; Integer q;— Both variablespandqare declared containing uninitialized reference slots.Step (b):
p = new Integer(5);— Allocates anIntegerobject with value ; referenceppoints to this instance.Step (c):
p = new Integer(6);— Allocates a newIntegerobject with value and updatespto point to it. The object containing value becomes unreferenced and eligible for garbage collection.Step (d):
q = p;— Variableqis assigned the memory reference stored inp. Bothpandqnow point to the exact sameIntegerobject holding (aliasing).Step (e):
q = new Integer(9);— Allocates a distinctIntegerobject with value and pointsqto it;premains pointing to .Step (f):
p = null;— Referencepis assignednull. Object holding loses its reference and becomes candidate for garbage collection.Step (g):
q = p;— Referenceqis set tonull(copied fromp). Object holding becomes unreferenced and eligible for garbage collection.
Reference-Based Linked Lists and Memory Dynamics
A reference-based linked list consists of discrete node objects linked dynamically via pointer references.
Unlike arrays, linked lists can dynamically grow or shrink in size on demand and do not require contiguous memory blocks or shifting elements during insertions and deletions.

Node Class Definition
In Java, a singly linked list node is represented as follows:
package List;
class Node {
Object item;
Node next;
Node(Object item) {
this.item = item;
this.next = null;
}
Node(Object item, Node next) {
this.item = item;
this.next = next;
}
}
Singly Linked List Operations
A singly linked list is unidirectional; traversal can only proceed sequentially from the
headpointer to the end (null).

Displaying Contents / Traversal
To display all items in a linked list, iterate using a reference variable
curr:
for (Node curr = head; curr != null; curr = curr.next) {
System.out.println(curr.item);
}
Pointer Determination for Sorted Insertion or Deletion
To locate insertion or deletion points in a sorted linked list, set
prevandcurrpointers:
for (prev = null, curr = head;
(curr != null) && (newValue.compareTo(curr.item) > 0);
prev = curr, curr = curr.next) {
// Iterates until reaching insertion/deletion position
}
Deleting a Node
Locate target node
currand preceding nodeprev.Disconnect target node:
prev.next = curr.next;.Clean up deleted node references:
curr.next = null; curr = null;.Special Case (Deleting Head Node):
head = head.next;.
Inserting a Node
Determine position (
prevandcurr).Create new node:
newNode = new Node(item);.Connect references:
newNode.next = curr; prev.next = newNode;.Special Case (Inserting at Head):
newNode.next = head; head = newNode;.
Linked List Variations: Tail Pointers, Circular Lists, Dummy Nodes, and Doubly Linked Lists
Tail References
Maintaining an explicit
tailpointer referencing the final node in the list allows constant time append operations at the end.Append Execution:
tail.next = new Node(request, null); tail = tail.next;.

Circular Linked Lists
In a circular linked list, the final node's
nextpointer references the first node (head) instead of storingnull.Every node in the circular chain has a defined successor.

Dummy Head Nodes
A dummy head node (or sentinel node) is a permanent placeholder node placed at index that precedes the first actual data node.
It is present even when the list is empty.
It eliminates edge cases during head insertions and deletions by guaranteeing that every data-containing node always has a valid preceding node
prev.

Doubly Linked Lists
Nodes in a doubly linked list contain two pointers:
nextreferencing the successor node, andprevreferencing the predecessor node.Enables bidirectional traversal across the linear structure.

Abstract Data Types: List Collections
A list is a linear collection that offers greater flexibility than strict stacks or queues, allowing insertion, access, and deletion at either end or at any intermediate point.
Three Primary Types of List Collections
Ordered Lists:
Elements are sorted automatically based on an inherent characteristic (e.g., names in alphabetical order, scores in ascending numeric order).
The elements themselves determine their precise location within the list.

Unordered Lists:
Elements maintain a specific sequence, but this order is not dictated by element characteristics.
The user or program determines position explicitly by inserting at the front, rear, or directly after an existing element.

Indexed Lists:
Elements are referenced directly by a zero-based numeric index position.
Index positions are recalculated and updated automatically whenever elements are added or removed.

Lists in the Java Collections Framework (Java API)
The Java API supports indexed and unordered list abstractions primarily through the
List<E>interface, implemented byArrayList<E>andLinkedList<E>.The standard Java API does not provide a direct class implementing an ordered list ADT out of the box.
LinkedList<E>Constructors & Key Methods:LinkedList(): Constructs an empty list.LinkedList(Collection<? extends E> c): Constructs a list containing elements of the specified collection in iteration order.add(E e)/addLast(E e): Appends specified element to the end of the list.add(int index, E element): Inserts element at specified index position.addFirst(E e): Inserts element at beginning of the list.clear(): Removes all elements from the list.get(int index): Returns element at specified index position.getFirst()/getLast(): Returns first / last element in the list.indexOf(Object o): Returns index of first occurrence of specified object, or if not found.remove()/removeFirst(): Retrieves and removes first element from the list.remove(int index): Removes element at specified index position.removeLast(): Removes and returns final element in the list.iterator(): Returns anIterator<E>object to traverse list elements sequentially (hasNext()tests for remaining items;next()retrieves the subsequent item).
The Queue ADT: Operations, Implementations, and Applications
A Queue is an Abstract Data Type that enforces a First-In, First-Out (FIFO) processing order.
Elements are added at the rear (back) and removed from the front.
Queue Interface Operations
Primary Operations:
enqueue(object): Inserts an element at the rear of the queue.object dequeue(): Removes and returns the element at the front of the queue.
Auxiliary Operations:
object peek()/first(): Returns the front element without removing it.integer size(): Returns the count of elements currently stored.boolean isEmpty(): Returnstrueif empty,falseotherwise.
Boundary Conditions: Calling
dequeue()orpeek()on an empty queue returnsnullor raises an exception.
Example Operation Sequence
Method Call | Return Value | Queue Contents (Front to Rear) |
|---|---|---|
| — |
|
| — |
|
|
|
|
| — |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| — |
|
| — |
|
|
|
|
| — |
|
| — |
|
|
|
|
Applications of Queues
Direct Applications: Print job queues, customer waiting lists, operating system task scheduling, shared resource access.
Indirect Applications: Breadth-first search algorithms, buffering data streams, components in complex data structures.
Queue Implementations
Naive Array Queue vs. Circular Array Queue
A naive array implementation shifts the front index rightward during deletions, resulting in "rightward drift" where array slots at lower indices become unusable, making a non-full queue appear prematurely full.

Circular Array Strategy: Uses an array of size combined with modular arithmetic.
f: Index of the front element.sz: Current total count of stored elements.The index
rfor inserting a new element is computed as:
Java Circular Array Queue Implementation (ArrayQueue<E>)
/** Implementation of the queue ADT using a fixed-length array. */
public class ArrayQueue<E> implements Queue<E> {
private E[] data; // generic array used for storage
private int f = 0; // index of the front element
private int sz = 0; // current number of elements
public static final int CAPACITY = 1000;
public ArrayQueue() { this(CAPACITY); }
@SuppressWarnings("unchecked")
public ArrayQueue(int capacity) {
data = (E[]) new Object[capacity]; // safe cast; compiler may give warning
}
public int size() { return sz; }
public boolean isEmpty() { return (sz == 0); }
/** Inserts an element at the rear of the queue. */
public void enqueue(E e) throws IllegalStateException {
if (sz == data.length) throw new IllegalStateException("Queue is full");
int avail = (f + sz) % data.length; // use modular arithmetic
data[avail] = e;
sz++;
}
/** Returns, but does not remove, the first element of the queue (null if empty). */
public E first() {
if (isEmpty()) return null;
return data[f];
}
/** Removes and returns the first element of the queue (null if empty). */
public E dequeue() {
if (isEmpty()) return null;
E answer = data[f];
data[f] = null; // dereference to help garbage collection
f = (f + 1) % data.length;
sz--;
return answer;
}
}
Reference-Based Queue Implementation
Uses a singly linked list maintaining two pointers:
firstNodepointing to the front node, andlastNodepointing to the rear node.enqueue(o)attaches new nodes afterlastNodein time.dequeue()removes nodes fromfirstNodein time.

The Stack ADT: Operations, Implementations, and Applications
A Stack is an Abstract Data Type operating under a Last-In, First-Out (LIFO) protocol.
Analogous to a spring-loaded cafeteria plate dispenser where the last item pushed onto the stack is the first item popped off.

Stack Operations
Primary Operations:
push(object): Inserts an element onto the top of the stack.object pop(): Removes and returns the top element from the stack.
Auxiliary Operations:
object top()/peek(): Returns the top element without removing it.integer size(): Returns the number of elements contained.boolean isEmpty(): Indicates whether the stack contains zero elements.
Stack Operations Trace
Method Call | Return Value | Stack Contents (Bottom to Top) |
|---|---|---|
| — |
|
| — |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| — |
|
| — |
|
|
|
|
| — |
|
|
|
|
|
|
|
| — |
|
| — |
|
|
|
|
Stack Implementations
Array-Based Stack
Uses an array
Sand an integer index variabletrepresenting the top index (initialized to when empty).Algorithm Push:
if t == S.length - 1 then throw Exception
t = t + 1
S[t] = o
```
- **Algorithm Pop**:
text if isEmpty() then return null o = S[t] S[t] = null t = t - 1 return o ```
Performance: Each operation runs in constant time . Space used is .
Limitation: Fixed capacity allocated upfront; pushing to a full array stack causes overflow.
Reference-Based Stack
Uses a singly linked list where a single external reference
toppoints to the head node.pushinserts a new node at the head (head = new Node(o, head)).popremoves the node at the head (o = head.item; head = head.next; return o).Provides unlimited dynamic capacity limited only by available memory.

Stack Applications
Direct Applications: Web browser page history back-tracking, text editor undo history chains, Java Virtual Machine (JVM) method execution stack frames.
Indirect Applications: Expression parsing, depth-first search, compiler syntax parsing.
Expression Conversion and Evaluation using Stacks
Infix to Postfix Conversion
In an infix expression, operators sit between operands (e.g., ).
In a postfix expression, operators follow their operands (e.g., ).
Conversion Rules:
Operands retain their exact original order.
Operators move to the right of their respective operands based on precedence.
All explicit parentheses are removed in postfix form.
Conversion Algorithm Pseudocode
create empty stack to hold Strings / characters
postfix = ""
for next token from infix:
if token is an operand (not "+", "-", "*", "/", "(", or ")") then
append token to postfix
else if token is "(" then
push "(" onto stack
else if token is ")" then
while top of stack is not "(" do
pop operator from stack and append to postfix
pop the "(" off the stack
else // token is an operator
while stack is not empty and top of stack is operator of equal or greater precedence than token and top is not "(" do
pop operator from stack and append to postfix
push token onto stack
while stack is not empty do
pop remaining operator from stack and append to postfix
Complete Conversion Trace: a - (b + c * d) / e

Token | Stack (Bottom to Top) |
| Notes / Action |
|---|---|---|---|
|
|
| Append operand |
|
|
| Push operator |
|
|
| Push open parenthesis |
|
|
| Append operand |
|
|
| Push operator |
|
|
| Append operand |
|
|
| Push higher precedence operator |
|
|
| Append operand |
|
|
| Pop operators until |
|
|
| Push |
|
|
| Append operand |
End |
|
| Pop all remaining operators |
Postfix Expression Evaluation Algorithm
Operands are pushed onto a stack when encountered.
When an operator is encountered, pop the top two operands:
operand2 = pop(),operand1 = pop(), apply operationresult = operand1 op operand2, and pushresultback onto stack.
Evaluation Trace for Infix 2 * (3 + 4) (Postfix 2 3 4 + *)

Key Entered | Calculator Action | Stack (Bottom to Top) |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Final expression evaluation result = .
Fundamentals of Algorithm Analysis
Algorithm analysis measures resource efficiency, focusing primarily on CPU running time and memory space relative to problem size .
Defining Problem Size
Searching: Size of search space pool.
Sorting: Total number of items to sort.
Text processing: Number of characters in string.
Dishwashing Example:
Problem size : Total dishes to wash.
Linear Strategy: Washing = , Drying = . Total time per dish = .
Inefficient Quadratic Strategy: Re-dry all previously washed dishes after washing each new dish.
Dishwashing Time Analysis Table
Number of Dishes () | Term (sec) | Term (sec) | Total Time (sec) |
|---|---|---|---|
Empirical (Experimental) Studies vs. Theoretical Analysis
Empirical Studies:
Involves writing fully runnable implementations, executing them over varying input sizes , measuring elapsed wall-clock time (
System.currentTimeMillis()), and plotting results.
long startTime = System.currentTimeMillis();
/* (run the algorithm) */
long endTime = System.currentTimeMillis();
long elapsed = endTime - startTime;
```
- **Limitations**: Requires complete implementation effort; results vary across different hardware/software environments; limited to test inputs used.
- **Theoretical Analysis**:
- Evaluates high-level pseudocode independent of hardware/software environments.
- Bounds running time mathematically as a function of problem size across all possible inputs.
# Mathematical Analysis & Primitive Operation Counting
## RAM Model & Primitive Operations
- Calculations assume the Random Access Machine (RAM) model where primitive operations take constant time .
- Primitive operations include:
- Assigning a value to a variable.
- Evaluating a arithmetic expression or boolean conditional.
- Comparing two numerical values.
- Indexing into an array cell.
- Calling a method.
- Returning from a method.
## Detailed Operation Count Analysis (`arrayMax`)
java public static double arrayMax(double[] data) { int n = data.length; // 2 operations (array length, assignment) double currentMax = data[0]; // 2 operations (indexing, assignment) for (int j = 1; j < n; j++) { // 2 + 2n operations (init, loop comparisons, increments) if (data[j] > currentMax) { // 2(n - 1) operations (indexing, comparison) currentMax = data[j]; // 0 to 2(n - 1) operations (conditional assignment) } } return currentMax; // 1 operation }
- **Worst-Case Count**: primitive operations.
- **Best-Case Count**: primitive operations.
- Bounded by linear growth: Let be time of fastest operation and be time of slowest operation:
# Asymptotic Notations: Big-O, Big-Omega, and Big-Theta

## Formal Definitions
1. **Big-O Notation ( - Upper Bound / Worst-Case)**:

2. **Big-Omega Notation ( - Lower Bound / Best-Case)**:

3. **Big-Theta Notation ( - Tight Bound / Average-Case)**:

## Mathematical Proofs for Big-O Bounding
- **Proof 1**: Show is .
- Set inequality: .
- Select constant , giving . For all , . Thus is .
- **Proof 2**: Prove is **not** .
- Assume . This contradicts the condition that is a fixed constant across arbitrary . Thus is not .
- **Proof 3**: Show is .
- Satisfied for and ().
- **Proof 4**: Show is .
- Satisfied for and ( for ).
# Analyzing Code Constructs & Growth Function Comparisons
## Seven Critical Growth Functions
| Function Label | Mathematical Form | Growth Category |
| :--- | :--- | :--- |
| Constant | / | |
| Logarithmic | | |
| Linear | / | |
| N-Log-N | | |
| Quadratic | / | |
| Cubic | / | |
| Exponential | | |
### Values of Seven Core Functions across Input Sizes
| | | | | | | |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
## Practical Sorting Efficiency: Insertion Sort vs. Merge Sort
- Insertion Sort running time: .
- Merge Sort running time: .
- **Sorting 1,000,000 items**:
- Insertion Sort requires .
- Merge Sort requires .
- **On a Faster Processor**:
- Insertion Sort requires .
- Merge Sort requires .
- Conclusion: Hardware speedups cannot compensate for poor asymptotic growth complexity ( vs ).
## Impact of Processor Speedup ( Processor Speedup)
| Algorithm | Time Complexity | Max Problem Size Before Speedup | Max Problem Size After Speedup |
| :--- | :--- | :--- | :--- |
| A | | | |
| B | | | |
| C | | | |
| D | | | |
## Rules for Code Complexity Analysis
1. **Single Loops**:
java for (int count = 0; count < n; count++) { // sequence of O(1) steps } ``` .
Logarithmic Loops:
count = 1;
while (count < n) {
count *= 2;
// sequence of O(1) steps
}
```
Loop executes times .
3. **Nested Loops**:
java for (int count = 0; count < n; count++) { for (int count2 = 1; count2 < n; count2 *= 2) { System.out.println(count + ", " + count2); } } ``` Outer loop executes times; inner loop executes times. Total efficiency = .
Nested Identical Loops:
java for (int count = 0; count < n; count++) { for (int count2 = 0; count2 < n; count2++) { // sequence of O(1) steps } } $$n \text{ iterations} \times n \text{ iterations} = O(n^2)$.Method Calls inside Loops:
To determine inner body complexity, multiply loop iteration count by the Big-O complexity of the method called inside.