Arrays
ArrayLists
Linked Lists
Stacks
Queues
An Array is a fixed-size sequential collection of elements of the same type. It provides a way to store multiple values in a single variable.For instance: int[] a = new int[10];
creates an array of integers that can hold 10 values.The array length is fixed at the time of construction, meaning it cannot be altered thereafter (e.g., length = 10).Elements are placed contiguously in memory, allowing for efficient access. Accessing any element using its index takes O(1) time, which is optimal.
/
Homogeneous: All components must be of the same type; however, an array of objects can hold any subclass type.
Initialization: When created, elements are initialized based on their type:
0 for primitive type arrays like int[]
or double[]
false for boolean arrays
null for an array of objects.
Dynamic Memory Allocation: Arrays are objects in Java, allowing them to be dynamically allocated during runtime, and they are automatically deallocated once they are no longer referenced.
Static Data Structure: The size of an array cannot change after its creation, leading to possible wasted space or overflow if the data exceeds its capacity.
Insertion/Deletion Complexity: Operations take O(n) time due to the need for shifting elements, making arrays less efficient for frequent insertions and deletions.
ArrayList
: A flexible, dynamic-sized array in Java that implements the List
interface, enabling it to expand and contract its size as needed during runtime.
Key Methods:
size()
: Returns the number of elements in the list.
isEmpty()
: Checks if the list is empty.
add(int index, E element)
: Inserts the specified element at the specified position in this list.
get(int index)
: Returns the element at the specified position in this list.
remove(int index)
: Removes the element at the specified position in this list.
set(int index, E element)
: Replaces the element at the specified position in this list with the specified element.
Problem: Write a Java pseudo code to create an ArrayList
of integers. Implement the following functionalities:
Add five integers to the list.
Remove the second integer from the list.
Retrieve and print the first and last integer from the list.
Pseudo Code:
class ArrayListDemo {
public static void main(String[] args) {
// Step 1: Create an ArrayList of integers
ArrayList<Integer> numbers = new ArrayList<>();
// Step 2: Add five integers to the list
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);
numbers.add(50);
// Step 3: Remove the second integer (index 1)
numbers.remove(1);
// Step 4: Retrieve and print the first (index 0) and last (current size - 1) integers
int firstNumber = numbers.get(0);
int lastNumber = numbers.get(numbers.size() - 1);
print("First Number: " + firstNumber);
print("Last Number: " + lastNumber);
}
}
This pseudo code illustrates how to manipulate an ArrayList
by performing basic operations such as adding, removing, and retrieving elements.
Space Complexity: O(n) for utilized space, with additional overhead due to internal resizing.
Performance:
size
, isEmpty
, get
, and set
methods run in O(1) time, providing efficiency for read operations.
add
and remove
operations run in O(n) time due to potential shifts in elements.
When the internal array is full, ArrayList
can be replaced with a larger one instead of throwing an exception.Two common strategies for resizing the array include:
Incremental: Increases by a constant number of spaces, which may lead to multiple resizing events.
Doubling: The array size is doubled, providing more space and amortizing the cost of resizing over multiple additions.
A Linked List is a fundamental concrete data structure consisting of a sequence of nodes where each node contains two components: an element and a reference (link) to the next node in the sequence.Types of Linked Lists:
Singly Linked List: Each node contains a link to the next node, allowing traversal in one direction.
Doubly Linked List: Nodes have links to both the next and previous nodes, enabling traversal in both directions.
A Singly Linked List consists of nodes, where each node contains a data element and a reference to the next node in the sequence. Below is a simple implementation in Java:
class SinglyLinkedList {
private static class Node {
int data;
Node next;
public Node(int data) {
this.data = data;
this.next = null;
}
}
private Node head;
public SinglyLinkedList() {
head = null;
}
// Method to add a new element to the end of the list
public void add(int data) {
Node newNode = new Node(data);
if (head == null) {
head = newNode; // If list is empty, new node becomes the head
} else {
Node current = head;
while (current.next != null) {
current = current.next; // Traverse to the last node
}
current.next = newNode; // Link the last node to the new node
}
}
// Method to remove the first element
public void removeFirst() {
if (head != null) {
head = head.next; // Remove the first node by moving the head to the next node
}
}
// Method to retrieve the last element
public int getLast() {
if (head == null) return -1; // If list is empty
Node current = head;
while (current.next != null) {
current = current.next; // Traverse to the last node
}
return current.data; // Return the last element
}
public static void main(String[] args) {
SinglyLinkedList list = new SinglyLinkedList();
list.add(10);
list.add(20);
list.add(30);
list.removeFirst(); // Removes 10
int lastNumber = list.getLast(); // Retrieves 30
System.out.println("Last Number: " + lastNumber);
}
}
Explanation:
Node Class: Represents each element in the list, containing a data variable and a reference to the next node.
SinglyLinkedList Class: Contains methods to manage the list, including adding, removing the first element, and getting the last element.
Add Method: Checks if the list is empty to insert the first node or traverses the list to insert a new node at the end.
RemoveFirst Method: Simply updates the head to the next node, effectively removing the first entered value.
GetLast Method: Traverses to the end of the list and returns the last node's data.
A Doubly Linked List consists of nodes that contain two references: one to the next node and another to the previous node. This allows traversal in both directions. Below is a simple implementation in Java:
class DoublyLinkedList {
private static class Node {
int data;
Node next;
Node prev;
public Node(int data) {
this.data = data;
this.next = null;
this.prev = null;
}
}
private Node head;
public DoublyLinkedList() {
head = null;
}
// Method to add a new element to the end of the list
public void add(int data) {
Node newNode = new Node(data);
if (head == null) {
head = newNode; // If list is empty, new node becomes the head
} else {
Node current = head;
while (current.next != null) {
current = current.next; // Traverse to the last node
}
current.next = newNode; // Link the last node to the new node
newNode.prev = current; // Link new node's prev to the current last node
}
}
// Method to remove the first element
public void removeFirst() {
if (head != null) {
head = head.next; // Move head to the next node
if (head != null) {
head.prev = null; // Update new head's prev reference
}
}
}
// Method to retrieve the last element
public int getLast() {
if (head == null) return -1; // If list is empty
Node current = head;
while (current.next != null) {
current = current.next; // Traverse to the last node
}
return current.data; // Return last element
}
public static void main(String[] args) {
DoublyLinkedList list = new DoublyLinkedList();
list.add(10);
list.add(20);
list.add(30);
list.removeFirst(); // Removes 10
int lastNumber = list.getLast(); // Retrieves 30
System.out.println("Last Number: " + lastNumber);
}
}
Explanation:
Node Class: Similar to the singly linked list but has an additional prev
reference to point to the previous node.
DoublyLinkedList Class: Methods to manage adding elements, removing the first element, and getting the last element.
Add Method: When a new node is added at the end, we also set the previous reference of the new node to the current last node.
RemoveFirst Method: Adjusts the head similarly, but also checks and updates the new head's prev
reference.
GetLast Method: Functions similarly as in the singly linked list.
private static class Node<E> {
private E element;
private Node<E> next;
}
Add First (E e): Inserts a new node with the element at the beginning.
Remove First (): Removes and returns the first node, adjusting the head pointer.
Add Last (E e): Inserts a new node at the end by traversing to the tail.
Remove Last (): Removes the last node, having the caveat of needing to traverse from the beginning in a singly linked list.
Pseudo Code Question for Linked List in Java:
Write a Java pseudo code to create a singly linked list of integers. Implement the following functionalities:
Add three integers to the list.
Remove the first integer from the list.
Retrieve and print the last integer from the list.
Pseudo Code:
class LinkedListDemo {
private static class Node {
int data;
Node next;
public Node(int data) {
this.data = data;
this.next = null;
}
}
private Node head;
public LinkedListDemo() {
head = null;
}
public void add(int data) {
Node newNode = new Node(data);
if (head == null) {
head = newNode;
} else {
Node current = head;
while (current.next != null) {
current = current.next;
}
current.next = newNode;
}
}
public void removeFirst() {
if (head != null) {
head = head.next;
}
}
public int getLast() {
if (head == null) return -1; // Indicating list is empty
Node current = head;
while (current.next != null) {
current = current.next;
}
return current.data;
}
public static void main(String[] args) {
LinkedListDemo list = new LinkedListDemo();
list.add(10);
list.add(20);
list.add(30);
list.removeFirst();
int lastNumber = list.getLast();
System.out.println("Last Number: " + lastNumber);
}
}
A singly linked list does not provide a constant-time method to update the tail pointer when adding or removing elements from the end of the list, leading to inefficiencies.
The Stack Abstract Data Type (ADT) is a collection of objects that follows the last-in-first-out (LIFO) principle. This means the last element added is the first to be removed. Stacks are commonly used in scenarios like function call management and expression evaluation.
Operations:
push(E e)
: Adds an object to the top of the stack.
pop()
: Removes the object from the top of the stack and returns it.
top()
: Returns the object at the top of the stack without removing it, allowing for peeking.
Pseudo Code Problem for Stacks in Java
Problem: Write a Java pseudo code to create a stack of integers. Implement the following functionalities:
Push three integers onto the stack.
Pop one integer from the stack.
Retrieve and print the top integer from the stack.
Pseudo Code:
class StackDemo {
private static class Stack {
private int[] array;
private int top;
public Stack(int size) {
array = new int[size];
top = -1; // indicates an empty stack
}
public void push(int e) {
array[++top] = e; // Increment top and add element
}
public int pop() {
return array[top--]; // Return top element and decrement top
}
public int top() {
return array[top]; // Peek at the top element
}
}
public static void main(String[] args) {
Stack stack = new Stack(5);
stack.push(10);
stack.push(20);
stack.push(30);
stack.pop(); // Remove the last element added (30)
int topElement = stack.top(); // Get the current top element (20)
System.out.println("Top Element: " + topElement);
}
}
Stacks can be implemented using a simple array. Each operation can be executed in constant time O(1), making it efficient for use cases like recursion handling or maintaining execution context.Limitation: The stack has a fixed size, which can lead to stack overflow if too many elements are added without adequate limits.
The Queue Abstract Data Type (ADT) is a collection of objects that follow the first-in-first-out (FIFO) principle, where the first element added is the first to be removed. Queues are essential for scheduling and handling tasks.
Operations:
enqueue(E e)
: Adds an object to the rear of the queue.
dequeue()
: Removes the object from the front of the queue and returns it.
first()
: Returns the object at the front without removing it, allowing for a peek at the next item to be processed.
Question: Write a Java pseudo code to implement a queue of integers. Include the functionality to:
Enqueue five integers into the queue.
Dequeue two integers from the queue.
Retrieve and print the front integer from the queue.
Pseudo Code:
class QueueDemo {
private static class Queue {
private int[] array;
private int front;
private int rear;
public Queue(int size) {
array = new int[size]; // Initialize the queue array
front = 0;
rear = -1;
}
public void enqueue(int e) {
array[++rear] = e; // Add element to the rear
}
public int dequeue() {
return array[front++]; // Remove and return the front element
}
public int first() {
return array[front]; // Peek at the front element
}
}
public static void main(String[] args) {
Queue queue = new Queue(5);
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
queue.enqueue(40);
queue.enqueue(50);
queue.dequeue(); // Removes 10 from the queue
queue.dequeue(); // Removes 20 from the queue
int frontElement = queue.first(); // retrieves the new front element (30)
System.out.println("Front Element: " + frontElement);
}
}
Queues can be implemented using an array. Each operation runs in O(1) time, making them suitable for high-performance situations.Limitation: Like stacks, queues also have a fixed size, which limits the number of elements that can be queued, risking overflow.
Each linear data structure has unique properties, methods, and limitations. A thorough understanding of these structures is crucial for effective data manipulation, algorithm implementation, and optimization in computer programming. Knowledge of when to use each structure can greatly impact performance and efficiency in software design.
More refined version of the node more basic review after reading the other notes these are just to see if you understand it more clearly
A Stack is a type of abstract data structure (ADT) that adheres to the Last-In-First-Out (LIFO) principle. This means that the last element added to the stack is the first element that gets removed. Stacks are crucial in various programming scenarios, such as:
Function call management: Keeping track of active functions/methods.
Expression evaluation: Managing operators and operands in computations.
Stacks are characterized by three primary operations:
push(E e): This operation adds an object e
to the top of the stack.
Example Pseudo Code:
Stack.push(5) // Adds 5 to the top of the stack
Stack.push(10) // Adds 10 to the top of the stack
pop(): This operation removes the object at the top of the stack and returns it.
Example Pseudo Code:
topElement = Stack.pop() // Removes and returns the top element
top(): This operation returns the object at the top of the stack without removing it, allowing the user to peek at it.
Example Pseudo Code:
topElement = Stack.top() // Returns the current top element
Stacks can be efficiently implemented using a simple array. With this implementation:
Each operation runs in constant time (O(1)), making it suitable for handling recursion and maintaining execution context.
Example Pseudo Code for Array-based Stack Implementation:
class Stack {
private int[] array;
private int top;
public Stack(int size) {
array = new int[size]; // Initialize the stack array
top = -1; // Indicates an empty stack
}
public void push(int e) {
array[++top] = e; // Increment top and add element
}
public int pop() {
return array[top--]; // Return top element and decrement top
}
public int top() {
return array[top]; // Peek at the top element
}
}
A significant limitation of stack implementations using arrays is that the stack has a fixed size. If many elements are pushed onto the stack, this can lead to stack overflowโan error that occurs when trying to add an item to a full stack.
A Queue is another type of abstract data structure (ADT) that follows the First-In-First-Out (FIFO) principle. Here, the first element added to the queue is the first to be removed. Queues are particularly useful for:
Task scheduling: Managing tasks in an orderly fashion.
Handling requests: In server-client interactions, requests are processed in the order they arrive.
Queues consist of three main operations:
enqueue(E e): This operation adds an object e
to the rear of the queue.
Example Pseudo Code:
Queue.enqueue(5) // Adds 5 to the rear of the queue
Queue.enqueue(10) // Adds 10 to the rear of the queue
dequeue(): This operation removes the object at the front of the queue and returns it.
Example Pseudo Code:
frontElement = Queue.dequeue() // Removes and returns the front element
first(): This operation returns the object at the front without removing it, allowing a look at the next item to be processed.
Example Pseudo Code:
frontElement = Queue.first() // Returns the current front element
Queues can also be implemented using an array, similar to stacks. This implementation allows:
Each operation to run in constant time (O(1)), making them efficient in high-demand applications.
Example Pseudo Code for Array-based Queue Implementation:
class Queue {
private int[] array;
private int front;
private int rear;
public Queue(int size) {
array = new int[size]; // Initialize the queue array
front = 0;
rear = -1;
}
public void enqueue(int e) {
array[++rear] = e; // Add element to the rear
}
public int dequeue() {
return array[front++]; // Remove and return the front element
}
public int first() {
return array[front]; // Peek at the front element
}
}
Similar to stacks, queues have a fixed size in array implementations. This limitation can also lead to overflow if the number of enqueued elements exceeds the allocated capacity.
Understanding the unique properties, operations, and limitations of stacks and queues is fundamental in effective data manipulation and algorithm optimization in programming. Mastery of when to utilize each structure provides a significant advantage in enhancing the performance and efficiency of software design.
In what scenarios would employing a stack be more beneficial than a queue?
Can you explain the strategies to manage stack overflow in real-world applications?
Are there alternative implementations for stacks and queues that could circumvent the issue of fixed sizing?
Could you provide a practical example illustrating how queues are utilized in real-world task scheduling?