{code}
Java Algorithm Practice Workbook: Implementations
Sorting Algorithms
/**
* Bubble Sort implementation in Java.
* Time Complexity: O(n^2)
* Space Complexity: O(1)
*/
public class BubbleSort {
public static void sort(int[] arr) {
int n = arr.length;
// Outer loop for the number of passes
for (int i = 0; i < n - 1; i++) {
// Inner loop for comparison and swapping
for (int j = 0; j < n - i - 1; j++) {
// Swap if the element found is greater than the next element
if (arr[j] > arr[j + 1]) {
// Standard swap operation
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}# PRESENTING CODE
1. Bubble Sort (Essential)
Description: Repeatedly swaps adjacent elements if out of order. This is often the first sorting algorithm taught due to its simplicity.
/**
* Insertion Sort implementation in Java.
* Time Complexity: O(n^2)
* Space Complexity: O(1)
*/
public class InsertionSort {
public static void sort(int[] arr) {
int n = arr.length;
// Start from the second element (index 1)
for (int i = 1; i < n; i++) {
int key = arr[i]; // Element to be inserted
int j = i - 1;
// Move elements of arr[0..i-1], that are greater than key,
// to one position ahead of their current position
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key; // Place the key in its correct position
}
}
}# PRESENTING CODE
2. Insertion Sort (Essential)
Description: Builds a sorted list by inserting elements in the correct position. It is efficient for small data sets or nearly sorted arrays.
/**
* Selection Sort implementation in Java.
* Time Complexity: O(n^2)
* Space Complexity: O(1)
*/
public class SelectionSort {
public static void sort(int[] arr) {
int n = arr.length;
// One by one move the boundary of the unsorted subarray
for (int i = 0; i < n - 1; i++) {
// Find the minimum element in the remaining unsorted array
int min_idx = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[min_idx]) {
min_idx = j;
}
}
// Swap the found minimum element with the first element
// of the unsorted subarray (arr[i])
int temp = arr[min_idx];
arr[min_idx] = arr[i];
arr[i] = temp;
}
}
}# PRESENTING CODE
3. Selection Sort
Description: Finds the minimum repeatedly and swaps it into the correct position.
/**
* Merge Sort implementation in Java.
* Time Complexity: O(n log n)
* Space Complexity: O(n)
*/
public class MergeSort {
// Main sort method
public static void sort(int[] arr) {
mergeSort(arr, new int[arr.length], 0, arr.length - 1);
}
private static void mergeSort(int[] arr, int[] temp, int low, int high) {
if (low < high) {
int middle = low + (high - low) / 2;
// Recursively sort the first and second halves
mergeSort(arr, temp, low, middle);
mergeSort(arr, temp, middle + 1, high);
// Merge the sorted halves
merge(arr, temp, low, middle, high);
}
}
// Merge two subarrays of arr[]
private static void merge(int[] arr, int[] temp, int low, int middle, int high) {
// Copy data to temp array
for (int i = low; i <= high; i++) {
temp[i] = arr[i];
}
int i = low; // Initial index of first subarray
int j = middle + 1; // Initial index of second subarray
int k = low; // Initial index of merged subarray
// Merge the temp arrays back into arr[low..high]
while (i <= middle && j <= high) {
if (temp[i] <= temp[j]) {
arr[k] = temp[i];
i++;
} else {
arr[k] = temp[j];
j++;
}
k++;
}
// Copy the remaining elements of the first half (if any)
while (i <= middle) {
arr[k] = temp[i];
k++;
i++;
}
}
}# PRESENTING CODE
4. Merge Sort (Essential)
Description: Divides the array in halves, sorts them, and merges the sorted halves recursively. This is a stable, comparison-based sort.
/**
* Quick Sort implementation in Java (using the last element as pivot).
* Time Complexity (Avg): O(n log n)
* Space Complexity: O(log n)
*/
public class QuickSort {
// Main sort method
public static void sort(int[] arr) {
quickSort(arr, 0, arr.length - 1);
}
private static void quickSort(int[] arr, int low, int high) {
if (low < high) {
// pi is partitioning index, arr[pi] is now at correct place
int pi = partition(arr, low, high);
// Recursively sort elements before and after partition
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
// This function takes last element as pivot, places the pivot element
// at its correct position in sorted array, and places all smaller
// (smaller than pivot) to left of pivot and all greater elements to right.
private static int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = (low - 1); // Index of smaller element
for (int j = low; j < high; j++) {
// If current element is smaller than or equal to pivot
if (arr[j] <= pivot) {
i++;
// swap arr[i] and arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// swap arr[i+1] and arr[high] (or pivot)
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return i + 1;
}
}# PRESENTING CODE
5. Quick Sort (Essential)
Description: Partitions the array around a chosen pivot element and recursively sorts the partitions. It's generally faster in practice than Merge Sort due to better cache performance.
/**
* Heap Sort implementation in Java.
* Time Complexity: O(n log n)
* Space Complexity: O(1)
*/
public class HeapSort {
// Main sort method
public static void sort(int[] arr) {
int n = arr.length;
// Build heap (rearrange array)
// Starts from the last non-leaf node
for (int i = n / 2 - 1; i >= 0; i--) {
heapify(arr, n, i);
}
// One by one extract an element from heap
for (int i = n - 1; i > 0; i--) {
// Move current root to end
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// call max heapify on the reduced heap
heapify(arr, i, 0);
}
}
// To heapify a subtree rooted with node i which is an index in arr[]
// n is size of heap
private static void heapify(int[] arr, int n, int i) {
int largest = i; // Initialize largest as root
int left = 2 * i + 1; // left = 2*i + 1
int right = 2 * i + 2; // right = 2*i + 2
// If left child is larger than root
if (left < n && arr[left] > arr[largest]) {
largest = left;
}
// If right child is larger than largest so far
if (right < n && arr[right] > arr[largest]) {
largest = right;
}
// If largest is not root
if (largest != i) {
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
// Recursively heapify the affected sub-tree
heapify(arr, n, largest);
}
}
}# PRESENTING CODE
6. Heap Sort (Essential)
Description: Builds a Max Heap from the data and repeatedly extracts the maximum element, placing it at the end of the array.
/**
* Counting Sort implementation in Java.
* Time Complexity: O(n+k)
* Space Complexity: O(k)
* Note: Assumes elements are non-negative and the range (k) is known.
*/
public class CountingSort {
public static void sort(int[] arr, int max) {
int n = arr.length;
int[] output = new int[n];
// 1. Create a count array (size max + 1) to store frequency
int[] count = new int[max + 1];
// 2. Store count of each element
for (int i = 0; i < n; i++) {
count[arr[i]]++;
}
// 3. Store cumulative count (modified count array)
// count[i] now contains the actual position of this element in the output array
for (int i = 1; i <= max; i++) {
count[i] += count[i - 1];
}
// 4. Build the output array (iterate backward for stability)
for (int i = n - 1; i >= 0; i--) {
output[count[arr[i]] - 1] = arr[i];
count[arr[i]]--;
}
// 5. Copy the output array to the original array
for (int i = 0; i < n; i++) {
arr[i] = output[i];
}
}
}# PRESENTING CODE
7. Counting Sort
Description: Counts the frequency of elements, suitable for sorting integers within a specific range. It's a non-comparison sort.
/**
* Radix Sort implementation in Java.
* Time Complexity: O(nk)
* Space Complexity: O(n+k)
*/
public class RadixSort {
// A utility function to get maximum value in arr[]
private static int getMax(int[] arr) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
// A function to do counting sort of arr[] according to the digit represented by exp.
// Exp is 10^i where i is the current digit place (1, 10, 100, ...)
private static void countSort(int[] arr, int exp) {
int n = arr.length;
int[] output = new int[n];
int[] count = new int[10]; // 10 possible digits (0-9)
// 1. Store count of occurrences in count[]
for (int i = 0; i < n; i++) {
count[(arr[i] / exp) % 10]++;
}
// 2. Change count[i] so that count[i] now contains actual position of this digit in output[]
for (int i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
// 3. Build the output array (iterate backward for stability)
for (int i = n - 1; i >= 0; i--) {
output[count[(arr[i] / exp) % 10] - 1] = arr[i];
count[(arr[i] / exp) % 10]--;
}
// 4. Copy the output array to arr[]
for (int i = 0; i < n; i++) {
arr[i] = output[i];
}
}
// Main sort method
public static void sort(int[] arr) {
// Find the maximum number to know the number of digits
int m = getMax(arr);
// Do counting sort for every digit. Note that exp is 10^i
for (int exp = 1; m / exp > 0; exp *= 10) {
countSort(arr, exp);
}
}
}# PRESENTING CODE
8. Radix Sort
Description: Sorts digits iteratively using a stable sort like Counting Sort, starting from the least significant digit (LSD).
Searching Algorithms
/**
* Linear Search implementation in Java.
* Time Complexity: O(n)
* Space Complexity: O(1)
*/
public class LinearSearch {
/**
* Searches for the target element sequentially.
* @param arr The array (can be unsorted).
* @param target The value to search for.
* @return The index of the target if found, or -1 otherwise.
*/
public static int search(int[] arr, int target) {
// Iterate through each element from start to end
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) {
return i; // Target found at index i
}
}
return -1; // Target not found
}
}# PRESENTING CODE
1. Linear Search (Essential)
Description: Checks each element sequentially. It is the simplest search algorithm and works on unsorted arrays.
/**
* Binary Search implementation in Java (Iterative).
* Time Complexity: O(log n)
* Space Complexity: O(1)
*/
public class BinarySearch {
/**
* Searches for the target element in a SORTED array.
* @param arr The array (MUST be sorted ascending).
* @param target The value to search for.
* @return The index of the target if found, or -1 otherwise.
*/
public static int search(int[] arr, int target) {
int low = 0;
int high = arr.length - 1;
while (low <= high) {
// Optimized calculation for mid to prevent integer overflow
int mid = low + (high - low) / 2;
if (arr[mid] == target) {
return mid; // Target found
} else if (arr[mid] < target) {
// Target is in the upper half
low = mid + 1;
} else {
// Target is in the lower half
high = mid - 1;
}
}
return -1; // Target not found
}
}# PRESENTING CODE
2. Binary Search (Essential)
Description: Divides the sorted array in halves repeatedly to find the target. This is the most efficient basic search algorithm for large, sorted datasets.
/**
* Jump Search implementation in Java.
* Time Complexity: O(sqrt(n))
* Space Complexity: O(1)
* Note: Uses Math.sqrt(n) as the optimal block size.
*/
public class JumpSearch {
/**
* Searches for the target element in a SORTED array using Jump Search.
* @param arr The array (MUST be sorted ascending).
* @param target The value to search for.
* @return The index of the target if found, or -1 otherwise.
*/
public static int search(int[] arr, int target) {
int n = arr.length;
// Optimal block size to jump is sqrt(n)
int step = (int)Math.sqrt(n);
// Find the block where the element is likely present
int prev = 0;
while (prev < n && arr[Math.min(step, n) - 1] < target) {
prev = step;
step += (int)Math.sqrt(n);
if (prev >= n)
return -1; // Target is beyond the last element
}
// Perform linear search for target in the block starting from 'prev'
while (prev < Math.min(step, n)) {
if (arr[prev] == target) {
return prev; // Target found
}
prev++;
}
return -1; // Target not found
}
}# PRESENTING CODE
3. Jump Search
Description: Jumps a fixed step size, then performs a linear search backward in the block where the element might reside. It is suitable for systems where jumping back costs less.
/**
* Interpolation Search implementation in Java.
* Time Complexity (Avg): O(log log n)
* Space Complexity: O(1)
* Note: Most effective on uniformly distributed sorted arrays.
*/
public class InterpolationSearch {
/**
* Searches for the target element in a SORTED array using Interpolation Search.
* @param arr The array (MUST be sorted ascending).
* @param target The value to search for.
* @return The index of the target if found, or -1 otherwise.
*/
public static int search(int[] arr, int target) {
int low = 0;
int high = arr.length - 1;
while (low <= high && target >= arr[low] && target <= arr[high]) {
// Check for the corner case where low and high are the same
if (low == high) {
if (arr[low] == target) return low;
return -1;
}
// Estimate the position (pos) using interpolation formula:
// pos = low + [(target - arr[low]) * (high - low) / (arr[high] - arr[low])]
long pos = low + (((long)target - arr[low]) * (high - low)) / (arr[high] - arr[low]);
int intPos = (int) pos;
if (arr[intPos] == target) {
return intPos; // Target found
}
if (arr[intPos] < target) {
// Target is in the upper part
low = intPos + 1;
} else {
// Target is in the lower part
high = intPos - 1;
}
}
return -1; // Target not found
}
}# PRESENTING CODE
4. Interpolation Search
Description: An improvement over Binary Search for uniformly distributed data. It estimates the position of the target using the value distribution.
Graph Algorithms
This section is crucial and involves the use of data structures such as queues, stacks, and maps for managing nodes and distances.
import java.util.*;
/**
* Breadth-First Search (BFS) implementation in Java.
* Time Complexity: O(V+E)
* Space Complexity: O(V)
*/
public class BFS {
// Adjacency List representation of the graph
private Map<Integer, List<Integer>> adj;
public BFS() {
adj = new HashMap<>();
}
// Add an edge to the graph (assuming undirected for simplicity)
public void addEdge(int u, int v) {
adj.computeIfAbsent(u, k -> new LinkedList<>()).add(v);
adj.computeIfAbsent(v, k -> new LinkedList<>()).add(u);
}
/**
* Performs BFS traversal starting from a given source node 's'.
* @param s The starting vertex.
*/
public void traverse(int s) {
// Mark all the vertices as not visited (initially set to false)
Set<Integer> visited = new HashSet<>();
// Create a queue for BFS
Queue<Integer> queue = new LinkedList<>();
// Mark the current node as visited and enqueue it
visited.add(s);
queue.add(s);
System.out.print("BFS Traversal starting from " + s + ": ");
while (queue.size() != 0) {
// Dequeue a vertex from queue and print it
s = queue.poll();
System.out.print(s + " ");
// Get all adjacent vertices of the dequeued vertex s.
// If an adjacent has not been visited, then mark it visited and enqueue it.
for (int neighbor : adj.getOrDefault(s, Collections.emptyList())) {
if (!visited.contains(neighbor)) {
visited.add(neighbor);
queue.add(neighbor);
}
}
}
System.out.println();
}
}# PRESENTING CODE
1. BFS (Breadth-First Search) (Essential)
Description: Traverses a graph or tree level by level using a Queue. It is used to find the shortest path in an unweighted graph.
import java.util.*;
/**
* Depth-First Search (DFS) implementation in Java (Recursive approach).
* Time Complexity: O(V+E)
* Space Complexity: O(V)
*/
public class DFS {
// Adjacency List representation of the graph
private Map<Integer, List<Integer>> adj;
public DFS() {
adj = new HashMap<>();
}
public void addEdge(int u, int v) {
adj.computeIfAbsent(u, k -> new LinkedList<>()).add(v);
adj.computeIfAbsent(v, k -> new LinkedList<>()).add(u);
}
private void DFSUtil(int v, Set<Integer> visited) {
// Mark the current node as visited and print it
visited.add(v);
System.out.print(v + " ");
// Recur for all the adjacent vertices
for (int neighbor : adj.getOrDefault(v, Collections.emptyList())) {
if (!visited.contains(neighbor)) {
DFSUtil(neighbor, visited);
}
}
}
/**
* Performs DFS traversal starting from a given source node 'v'.
* @param v The starting vertex.
*/
public void traverse(int v) {
Set<Integer> visited = new HashSet<>();
System.out.print("DFS Traversal starting from " + v + ": ");
DFSUtil(v, visited);
System.out.println();
}
}# PRESENTING CODE
2. DFS (Depth-First Search) (Essential)
Description: Traverses deep into branches using recursion or an explicit stack. It's used for topological sorting, finding connected components, and path finding.
import java.util.*;
/**
* Dijkstra's Algorithm implementation in Java.
* Finds the shortest path from a source to all other nodes.
*/
public class Dijkstra {
// Helper class to store a vertex and its current distance
private static class Node implements Comparable<Node> {
int vertex;
int distance;
public Node(int vertex, int distance) {
this.vertex = vertex;
this.distance = distance;
}
@Override
public int compareTo(Node other) {
return Integer.compare(this.distance, other.distance);
}
}
// Adjacency list: Map<Vertex, List<Edge>> where Edge is represented as a Pair<Neighbor, Weight>
private Map<Integer, List<int[]>> adj;
public Dijkstra() {
adj = new HashMap<>();
}
// Edge: int[0] = neighbor, int[1] = weight
public void addEdge(int u, int v, int weight) {
adj.computeIfAbsent(u, k -> new LinkedList<>()).add(new int[]{v, weight});
}
/**
* Finds the shortest path distances from the source to all vertices.
* @param startVertex The source vertex.
* @param totalVertices The total number of vertices in the graph.
* @return Map of Vertex -> Shortest Distance.
*/
public Map<Integer, Integer> findShortestPaths(int startVertex, int totalVertices) {
// Map to store the shortest distance from the source to all vertices
Map<Integer, Integer> distances = new HashMap<>();
// Initialize distances: 0 for source, Infinity for others
for(int i = 0; i < totalVertices; i++) {
distances.put(i, Integer.MAX_VALUE);
}
distances.put(startVertex, 0);
// PriorityQueue to select the vertex with the minimum distance
PriorityQueue<Node> pq = new PriorityQueue<>();
pq.add(new Node(startVertex, 0));
while (!pq.isEmpty()) {
Node current = pq.poll();
int u = current.vertex;
int dist = current.distance;
// Optimization: If the distance is already shorter, skip
if (dist > distances.get(u)) continue;
// Iterate through neighbors
for (int[] edge : adj.getOrDefault(u, Collections.emptyList())) {
int v = edge[0]; // Neighbor vertex
int weight = edge[1]; // Edge weight
// Relaxation step: If a shorter path is found to v through u
if (distances.get(u) + weight < distances.get(v)) {
distances.put(v, distances.get(u) + weight);
pq.add(new Node(v, distances.get(v)));
}
}
}
return distances;
}
}# PRESENTING CODE
3. Dijkstra's Algorithm (Essential)
Description: Finds the shortest paths from a single source vertex to all other vertices in a weighted graph with non-negative edge weights. It typically uses a Priority Queue for efficiency.
import java.util.*;
/**
* Kruskal's Algorithm implementation in Java.
* Finds the Minimum Spanning Tree (MST).
*/
public class Kruskal {
// Edge representation
private static class Edge implements Comparable<Edge> {
int src, dest, weight;
public Edge(int src, int dest, int weight) {
this.src = src;
this.dest = dest;
this.weight = weight;
}
@Override
public int compareTo(Edge other) {
return Integer.compare(this.weight, other.weight);
}
}
// A basic implementation of Disjoint Set Union (DSU) or Union-Find
private static class DisjointSet {
int[] parent;
public DisjointSet(int n) {
parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i; // Every node is its own parent initially
}
}
// Find operation with path compression
public int find(int i) {
if (parent[i] == i)
return i;
return parent[i] = find(parent[i]);
}
// Union operation
public void union(int i, int j) {
int rootI = find(i);
int rootJ = find(j);
if (rootI != rootJ) {
parent[rootJ] = rootI; // Merge sets
}
}
}
/**
* Finds the MST using Kruskal's algorithm.
* @param edges List of all edges in the graph.
* @param V Total number of vertices.
* @return List of edges in the MST.
*/
public static List<Edge> findMST(List<Edge> edges, int V) {
List<Edge> mst = new ArrayList<>();
// 1. Sort all edges in non-decreasing order of their weight
Collections.sort(edges);
// 2. Initialize Disjoint Set (Union-Find)
DisjointSet ds = new DisjointSet(V);
// 3. Iterate through sorted edges and apply Union-Find
for (Edge edge : edges) {
int rootSrc = ds.find(edge.src);
int rootDest = ds.find(edge.dest);
// If including this edge does NOT form a cycle (they are in different sets)
if (rootSrc != rootDest) {
mst.add(edge);
ds.union(edge.src, edge.dest); // Union the two sets
}
// Optimization: MST will have V-1 edges
if (mst.size() == V - 1) break;
}
return mst;
}
}# PRESENTING CODE
4. Kruskal's Algorithm (Essential)
Description: Finds the Minimum Spanning Tree (MST) using a Union-Find data structure. It sorts all edges by weight and includes the edge if it doesn't form a cycle.
import java.util.*;
/**
* Prim's Algorithm implementation in Java.
* Finds the Minimum Spanning Tree (MST).
*/
public class Prim {
// Helper class to store a vertex and its key value
private static class Node implements Comparable<Node> {
int vertex;
int key; // The weight of the edge to connect this vertex to MST
public Node(int vertex, int key) {
this.vertex = vertex;
this.key = key;
}
@Override
public int compareTo(Node other) {
return Integer.compare(this.key, other.key);
}
}
/**
* Finds the MST using Prim's algorithm.
* @param V Total number of vertices.
* @param adj Adjacency list: Map<Vertex, List<Edge>> where Edge is int[]{neighbor, weight}
* @param startVertex The starting vertex (usually 0).
* @return Array where parent[i] is the parent of i in MST.
*/
public static int[] findMST(int V, Map<Integer, List<int[]>> adj, int startVertex) {
// key[i] holds the minimum weight to connect i to MST
int[] key = new int[V];
// parent[i] stores the parent of i in MST
int[] parent = new int[V];
// mstSet[i] is true if vertex i is included in MST
boolean[] mstSet = new boolean[V];
Arrays.fill(key, Integer.MAX_VALUE);
Arrays.fill(parent, -1);
// PriorityQueue to pick the minimum key vertex
PriorityQueue<Node> pq = new PriorityQueue<>();
// Start with the source vertex
key[startVertex] = 0;
pq.add(new Node(startVertex, 0));
while (!pq.isEmpty()) {
int u = pq.poll().vertex;
if (mstSet[u]) continue; // Already processed
mstSet[u] = true;
// Iterate over all neighbors of u
for (int[] edge : adj.getOrDefault(u, Collections.emptyList())) {
int v = edge[0]; // Neighbor vertex
int weight = edge[1]; // Edge weight
// If v is not yet in MST and the weight of (u, v) is smaller than key[v]
if (!mstSet[v] && weight < key[v]) {
parent[v] = u;
key[v] = weight;
pq.add(new Node(v, key[v]));
}
}
}
// parent array holds the MST structure (parent[0] is -1)
return parent;
}
}# PRESENTING CODE
5. Prim's Algorithm
Description: Builds the MST by adding the smallest edges incrementally, starting from an arbitrary vertex. Similar to Dijkstra's, it uses a Priority Queue to pick the next edge.
import java.util.Arrays;
/**
* Floyd-Warshall Algorithm implementation in Java.
* Finds the shortest path between all pairs of vertices.
*/
public class FloydWarshall {
final static int INF = 99999; // Represents infinity
/**
* Computes all-pairs shortest paths.
* @param graph The adjacency matrix representation of the graph.
* @param V Total number of vertices.
* @return The distance matrix with all-pairs shortest paths.
*/
public static int[][] findShortestPaths(int[][] graph, int V) {
int[][] dist = new int[V][V];
int i, j, k;
// Initialize the distance matrix with the input graph
for (i = 0; i < V; i++) {
System.arraycopy(graph[i], 0, dist[i], 0, V);
}
// Core algorithm: Pick all vertices as intermediate vertices
for (k = 0; k < V; k++) {
// Pick all vertices as source one by one
for (i = 0; i < V; i++) {
// Pick all vertices as destination one by one
for (j = 0; j < V; j++) {
// If vertex k is on the shortest path from i to j,
// then update the value of dist[i][j]
if (dist[i][k] != INF && dist[k][j] != INF
&& dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
// Note: Distances[i][i] being negative indicates a negative cycle.
return dist;
}
}# PRESENTING CODE
6. Floyd-Warshall Algorithm
Description: Computes the shortest paths between all pairs of vertices. It uses dynamic programming and can handle negative edge weights, but not negative cycles.
import java.util.Arrays;
/**
* Bellman-Ford Algorithm implementation in Java.
* Handles negative edge weights and detects negative cycles.
*/
public class BellmanFord {
// Edge representation for simplicity
private static class Edge {
int src, dest, weight;
public Edge(int src, int dest, int weight) {
this.src = src; this.dest = dest; this.weight = weight;
}
}
/**
* Finds the shortest path distances from a single source.
* @param V Total number of vertices.
* @param edges Array of all edges in the graph.
* @param startVertex The source vertex.
* @return Array of shortest distances, or null if a negative cycle is detected.
*/
public static int[] findShortestPaths(int V, List<Edge> edges, int startVertex) {
int[] dist = new int[V];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[startVertex] = 0;
// Step 1: Relax all edges V-1 times.
// A path can have at most V-1 edges.
for (int i = 1; i < V; i++) {
for (Edge edge : edges) {
int u = edge.src;
int v = edge.dest;
int weight = edge.weight;
if (dist[u] != Integer.MAX_VALUE && dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
}
}
}
// Step 2: Check for negative-weight cycles (V-th relaxation)
for (Edge edge : edges) {
int u = edge.src;
int v = edge.dest;
int weight = edge.weight;
if (dist[u] != Integer.MAX_VALUE && dist[u] + weight < dist[v]) {
// Graph contains negative weight cycle
System.out.println("Graph contains negative weight cycle! Cannot compute shortest paths.");
return null;
}
}
return dist;
}
}# PRESENTING CODE
7. Bellman-Ford Algorithm
Description: Handles graphs with negative edge weights. It can also detect if a negative cycle is reachable from the source.
import java.util.*;
/**
* Topological Sort implementation in Java (Kahn's Algorithm - using BFS).
* Only works on Directed Acyclic Graphs (DAGs).
* Time Complexity: O(V+E)
* Space Complexity: O(V)
*/
public class TopologicalSort {
// Adjacency List
private Map<Integer, List<Integer>> adj;
public TopologicalSort() {
adj = new HashMap<>();
}
// Assumes directed edge u -> v
public void addEdge(int u, int v) {
adj.computeIfAbsent(u, k -> new LinkedList<>()).add(v);
}
/**
* Performs Topological Sort using Kahn's algorithm.
* @param V Total number of vertices.
* @return List of vertices in topological order, or empty if a cycle exists.
*/
public List<Integer> sort(int V) {
int[] inDegree = new int[V];
// 1. Calculate in-degrees for all vertices
for (int u = 0; u < V; u++) {
for (int v : adj.getOrDefault(u, Collections.emptyList())) {
inDegree[v]++;
}
}
// 2. Initialize queue with all vertices having in-degree 0
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < V; i++) {
if (inDegree[i] == 0) {
queue.add(i);
}
}
List<Integer> topOrder = new ArrayList<>();
int count = 0;
// 3. Process vertices
while (!queue.isEmpty()) {
int u = queue.poll();
topOrder.add(u);
count++;
// Decrease in-degree of all neighbors (adjacent vertices)
for (int v : adj.getOrDefault(u, Collections.emptyList())) {
inDegree[v]--;
// If in-degree becomes 0, enqueue it
if (inDegree[v] == 0) {
queue.add(v);
}
}
}
// 4. Check for cycle
if (count != V) {
// A cycle exists in the graph, as not all vertices were processed.
return Collections.emptyList();
}
return topOrder;
}
}# PRESENTING CODE
8. Topological Sort (Essential)
Description: Orders vertices in a Directed Acyclic Graph (DAG) so that dependencies precede dependents. It can be implemented using either DFS or by calculating in-degrees (Kahn's Algorithm).
Dynamic Programming
Dynamic Programming (DP) is a technique that solves complex problems by breaking them down into simpler subproblems and storing the results of those subproblems to avoid recomputing them (memoization or tabulation).
/**
* Fibonacci sequence calculation using Dynamic Programming (Tabulation).
* Time Complexity: O(n)
* Space Complexity: O(n)
*/
public class FibonacciDP {
/**
* Calculates the n-th Fibonacci number.
* @param n The index of the Fibonacci number to find (n >= 0).
* @return The n-th Fibonacci number.
*/
public static int calculate(int n) {
if (n <= 1) {
return n;
}
// 1. Create a DP array (table) to store results of subproblems
int[] dp = new int[n + 1];
// 2. Base cases
dp[0] = 0;
dp[1] = 1;
// 3. Fill the table using the recurrence relation: F(i) = F(i-1) + F(i-2)
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
}# PRESENTING CODE
1. Fibonacci (Essential)
Description: Calculates the n-th number in the Fibonacci sequence. The DP approach stores previous results (bottom-up/tabulation) to achieve linear time complexity.
/**
* Longest Common Subsequence (LCS) calculation using Dynamic Programming.
* Time Complexity: O(m*n)
* Space Complexity: O(m*n)
*/
public class LCS {
/**
* Finds the length of the Longest Common Subsequence of two strings.
* @param str1 The first string.
* @param str2 The second string.
* @return The length of the LCS.
*/
public static int findLCSLength(String str1, String str2) {
int m = str1.length();
int n = str2.length();
// dp[i][j] stores the length of LCS of str1[0..i-1] and str2[0..j-1]
int[][] dp = new int[m + 1][n + 1];
// Fill the DP table (tabulation)
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
if (i == 0 || j == 0) {
dp[i][j] = 0; // Base case: one string is empty
}
// If characters match, add 1 to the diagonal entry
else if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
dp[i][j] = 1 + dp[i - 1][j - 1];
}
// If characters don't match, take the maximum from the left or above
else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
}# PRESENTING CODE
2. LCS (Longest Common Subsequence) (Essential)
Description: Finds the length of the longest sequence common to two strings, where the sequence does not have to be contiguous.
/**
* 0/1 Knapsack Problem solution using Dynamic Programming.
*/
public class Knapsack01 {
public static int solve(int W, int[] weights, int[] values, int n) {
int[][] dp = new int[n + 1][W + 1];
for (int i = 0; i <= n; i++) {
for (int w = 0; w <= W; w++) {
if (i == 0 || w == 0) {
dp[i][w] = 0;
}
else if (weights[i - 1] <= w) {
int include = values[i - 1] + dp[i - 1][w - weights[i - 1]];
int exclude = dp[i - 1][w];
dp[i][w] = Math.max(include, exclude);
}
else {
dp[i][w] = dp[i - 1][w];
}
}
}
return dp[n][W];
}
}# PRESENTING CODE
3. Knapsack 0/1 (Essential)
Description: Maximizes the total value of items that can be placed in a knapsack with a specific weight limit.
import java.util.Arrays;
/**
* Longest Increasing Subsequence (LIS) using Dynamic Programming (O(n^2) approach).
*/
public class LIS_O_n2 {
public static int findLISLength(int[] arr) {
int n = arr.length;
if (n == 0) return 0;
int[] dp = new int[n];
Arrays.fill(dp, 1);
int maxLength = 1;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (arr[i] > arr[j] && dp[i] < dp[j] + 1) {
dp[i] = dp[j] + 1;
}
}
maxLength = Math.max(maxLength, dp[i]);
}
return maxLength;
}
}# PRESENTING CODE
4. Longest Increasing Subsequence (LIS)
Description: Finds the length of the longest subsequence in which elements appear in strictly increasing order.
import java.util.Arrays;
/**
* Coin Change problem: Minimum number of coins to make a given amount.
*/
public class CoinChangeMin {
public static int getMinCoins(int[] coins, int amount) {
int max = amount + 1;
int[] dp = new int[amount + 1];
Arrays.fill(dp, max);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int coin : coins) {
if (coin <= i) {
dp[i] = Math.min(dp[i], 1 + dp[i - coin]);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
}# PRESENTING CODE
5. Coin Change (Minimum Coins)
Description: Finds the minimum number of coins needed to make a specific amount, given a set of coin denominations.
/**
* Edit Distance (Minimum operations to transform one string to another) using DP.
*/
public class EditDistance {
public static int calculate(String str1, String str2) {
int m = str1.length();
int n = str2.length();
int[][] dp = new int[m + 1][n + 1];
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
if (i == 0) {
dp[i][j] = j;
}
else if (j == 0) {
dp[i][j] = i;
}
else if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
}
else {
dp[i][j] = 1 + Math.min(dp[i][j - 1],
Math.min(dp[i - 1][j],
dp[i - 1][j - 1]));
}
}
}
return dp[m][n];
}
}# PRESENTING CODE
6. Edit Distance (Essential)
Description: Calculates the minimum number of operations (insertions, deletions, or replacements) required to transform one string into another.
/**
* Matrix Chain Multiplication (MCM) using Dynamic Programming.
*/
public class MatrixChainMultiplication {
public static int solve(int[] dims) {
int n = dims.length - 1;
int[][] dp = new int[n + 1][n + 1];
for (int i = 1; i <= n; i++) {
dp[i][i] = 0;
}
for (int l = 2; l <= n; l++) {
for (int i = 1; i <= n - l + 1; i++) {
int j = i + l - 1;
dp[i][j] = Integer.MAX_VALUE;
for (int k = i; k < j; k++) {
int cost = dp[i][k] + dp[k + 1][j] + dims[i - 1] * dims[k] * dims[j];
if (cost < dp[i][j]) {
dp[i][j] = cost;
}
}
}
}
return dp[1][n];
}
}# PRESENTING CODE
7. Matrix Chain Multiplication
Description: Determines the optimal order for multiplying a sequence of matrices to minimize the total number of scalar multiplications.
/**
* Subset Sum Problem solution using Dynamic Programming.
*/
public class SubsetSum {
public static boolean isSubsetSum(int[] set, int sum) {
int n = set.length;
boolean[][] dp = new boolean[n + 1][sum + 1];
for (int i = 0; i <= n; i++) {
dp[i][0] = true;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= sum; j++) {
dp[i][j] = dp[i - 1][j];
if (j >= set[i - 1]) {
dp[i][j] = dp[i][j] || dp[i - 1][j - set[i - 1]];
}
}
}
return dp[n][sum];
}
}# PRESENTING CODE
8. Subset Sum
Description: Determines whether there exists a subset of non-negative integers that sums up to a given target value.
String Algorithms
/**
* Knuth-Morris-Pratt (KMP) Algorithm in Java.
*/
public class KMP {
private static int[] computeLPSArray(String pattern) {
int m = pattern.length();
int[] lps = new int[m];
int length = 0;
int i = 1;
lps[0] = 0;
while (i < m) {
if (pattern.charAt(i) == pattern.charAt(length)) {
length++;
lps[i] = length;
i++;
} else {
if (length != 0) {
length = lps[length - 1];
} else {
lps[i] = 0;
i++;
}
}
}
return lps;
}
public static int search(String text, String pattern) {
int n = text.length();
int m = pattern.length();
if (m == 0) return 0;
int[] lps = computeLPSArray(pattern);
int i = 0;
int j = 0;
while (i < n) {
if (pattern.charAt(j) == text.charAt(i)) {
i++; j++;
}
if (j == m) {
return i - j;
}
else if (i < n && pattern.charAt(j) != text.charAt(i)) {
if (j != 0) {
j = lps[j - 1];
} else {
i++;
}
}
}
return -1;
}
}# PRESENTING CODE
1. KMP (Knuth-Morris-Pratt) (Essential)
Description: Preprocesses the pattern to determine the Longest Proper Prefix which is also a Suffix (LPS), enabling skip optimization.
/**
* Rabin-Karp Algorithm implementation in Java.
*/
public class RabinKarp {
private final static int Q = 101;
private final static int D = 256;
public static int search(String pattern, String text) {
int m = pattern.length();
int n = text.length();
long h = 1;
long patternHash = 0;
long textHash = 0;
for (int i = 0; i < m - 1; i++) h = (h * D) % Q;
for (int i = 0; i < m; i++) {
patternHash = (D * patternHash + pattern.charAt(i)) % Q;
textHash = (D * textHash + text.charAt(i)) % Q;
}
for (int i = 0; i <= n - m; i++) {
if (patternHash == textHash) {
int j = 0;
for (j = 0; j < m; j++) {
if (text.charAt(i + j) != pattern.charAt(j)) break;
}
if (j == m) return i;
}
if (i < n - m) {
textHash = (D * (textHash - text.charAt(i) * h) + text.charAt(i + m)) % Q;
if (textHash < 0) textHash = (textHash + Q);
}
}
return -1;
}
}# PRESENTING CODE
2. Rabin-Karp
Description: Uses a rolling hash technique for substring search, checking characters only on hash matches.
/**
* Manacher's Algorithm implementation in Java.
*/
public class Manacher {
private static String preprocess(String s) {
StringBuilder sb = new StringBuilder("#");
for (char c : s.toCharArray()) sb.append(c).append("#");
return sb.toString();
}
public static int findLPSLength(String s) {
String T = preprocess(s);
int n = T.length();
int[] P = new int[n];
int center = 0;
int rightBoundary = 0;
int maxLen = 0;
for (int i = 1; i < n - 1; i++) {
int mirror = 2 * center - i;
if (i < rightBoundary) P[i] = Math.min(rightBoundary - i, P[mirror]);
while (i + 1 + P[i] < n && i - 1 - P[i] >= 0 &&
T.charAt(i + 1 + P[i]) == T.charAt(i - 1 - P[i])) {
P[i]++;
}
if (i + P[i] > rightBoundary) {
center = i;
rightBoundary = i + P[i];
}
maxLen = Math.max(maxLen, P[i]);
}
return maxLen;
}
}# PRESENTING CODE
3. Manacher's Algorithm (Essential)
Description: Finds the longest palindromic substring in linear time by exploiting symmetry.
import java.util.HashMap;
import java.util.Map;
/**
* Trie (Prefix Tree) implementation in Java.
*/
public class Trie {
private class TrieNode {
Map<Character, TrieNode> children = new HashMap<>();
boolean isEndOfWord = false;
}
private TrieNode root = new TrieNode();
public void insert(String word) {
TrieNode current = root;
for (char ch : word.toCharArray()) {
current.children.putIfAbsent(ch, new TrieNode());
current = current.children.get(ch);
}
current.isEndOfWord = true;
}
public boolean search(String word) {
TrieNode current = root;
for (char ch : word.toCharArray()) {
current = current.children.get(ch);
if (current == null) return false;
}
return current.isEndOfWord;
}
}# PRESENTING CODE
4. Trie Construction
Description: A prefix tree used for efficient storage and retrieval of strings based on their prefixes.
/**
* Z-Algorithm implementation in Java.
*/
public class ZAlgorithm {
public static int[] computeZArray(String s) {
int n = s.length();
int[] z = new int[n];
int l = 0, r = 0;
for (int i = 1; i < n; i++) {
if (i > r) {
l = r = i;
while (r < n && s.charAt(r - l) == s.charAt(r)) r++;
z[i] = r - l;
r--;
} else {
int k = i - l;
if (z[k] < r - i + 1) {
z[i] = z[k];
} else {
l = i;
while (r < n && s.charAt(r - l) == s.charAt(r)) r++;
z[i] = r - l;
r--;
}
}
}
return z;
}
}# PRESENTING CODE
5. Z-Algorithm
Description: Computes the Z-array where Z[i] is the length of the longest substring starting at i that is also a prefix of the string.
import java.util.Arrays;
/**
* Boyer-Moore Algorithm implementation in Java.
*/
public class BoyerMoore {
private final static int ALPHABET_SIZE = 256;
private static int[] buildBadCharTable(String pattern) {
int m = pattern.length();
int[] last = new int[ALPHABET_SIZE];
Arrays.fill(last, -1);
for (int i = 0; i < m; i++) last[pattern.charAt(i)] = i;
return last;
}
public static int search(String text, String pattern) {
int n = text.length();
int m = pattern.length();
if (m == 0) return 0;
int[] badChar = buildBadCharTable(pattern);
int s = 0;
while (s <= (n - m)) {
int j = m - 1;
while (j >= 0 && pattern.charAt(j) == text.charAt(s + j)) j--;
if (j < 0) {
return s;
} else {
int lastOcc = badChar[text.charAt(s + j)];
s += Math.max(1, j - lastOcc);
}
}
return -1;
}
}# PRESENTING CODE
6. Boyer-Moore
Description: Highly efficient string searching that uses mismatch heuristics (Bad Character Rule and Good Suffix Rule) to skip large parts of the text.
Math & Number Theory
/**
* Euclidean Algorithm for Greatest Common Divisor (GCD) (Iterative).
*/
public class EuclideanGCD {
public static int gcd(int a, int b) {
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
}# PRESENTING CODE
1. GCD (Euclidean Algorithm) (Essential)
Description: Computes the Greatest Common Divisor (GCD) of two non-negative integers a and b iteratively.
/**
* Extended Euclidean Algorithm implementation (Recursive).
*/
public class ExtendedEuclidean {
public static class GCDResult {
int gcd, x, y;
public GCDResult(int gcd, int x, int y) {
this.gcd = gcd; this.x = x; this.y = y;
}
}
public static GCDResult extendedGcd(int a, int b) {
if (a == 0) {
return new GCDResult(b, 0, 1);
}
GCDResult result = extendedGcd(b % a, a);
int gcd = result.gcd;
int x1 = result.x;
int y1 = result.y;
int x = y1 - (b / a) * x1;
int y = x1;
return new GCDResult(gcd, x, y);
}
}# PRESENTING CODE
2. Extended Euclidean Algorithm
Description: Computes $\text{gcd}(a, b)$ and finds coefficients $x$ and $y$ such that $ax + by = \text{gcd}(a, b)$.
import java.util.Arrays;
/**
* Sieve of Eratosthenes implementation in Java.
*/
public class SieveOfEratosthenes {
public static boolean[] sieve(int n) {
boolean[] isPrime = new boolean[n + 1];
Arrays.fill(isPrime, true);
isPrime[0] = false; isPrime[1] = false;
for (int p = 2; p * p <= n; p++) {
if (isPrime[p]) {
for (int i = p * p; i <= n; i += p) {
isPrime[i] = false;
}
}
}
return isPrime;
}
}# PRESENTING CODE
3. Sieve of Eratosthenes (Essential)
Description: Generates all prime numbers up to a specified integer $n$ by iteratively marking the multiples of each prime.
/**
* Modular Exponentiation (Power) using exponentiation by squaring (Iterative).
*/
public class ModularExponentiation {
public static long power(long base, long exponent, long modulus) {
long result = 1;
base %= modulus;
if (base == 0) return 0;
while (exponent > 0) {
if ((exponent & 1) == 1) {
result = (result * base) % modulus;
}
exponent >>= 1;
base = (base * base) % modulus;
}
return result;
}
}# PRESENTING CODE
4. Modular Exponentiation (Essential)
Description: Computes $(base^n) \bmod modulus$ efficiently using exponentiation by squaring.
/**
* Application of Fermat's Little Theorem: Finding the Modular Multiplicative Inverse.
*/
public class ModularInverse {
// Note: Requires ModularExponentiation.power (or the code embedded)
public static long inverse(long a, long p) {
// Assume p is prime
a %= p;
long result = 1;
long exponent = p - 2;
while (exponent > 0) {
if ((exponent & 1) == 1) {
result = (result * a) % p;
}
exponent >>= 1;
a = (a * a) % p;
}
return result;
}
}# PRESENTING CODE
5. Fermat's Little Theorem
Description: Basis for modular inverses when the modulus $p$ is prime: $a^{p-2} \equiv a^{-1} \pmod{p}$.
import java.math.BigInteger;
/**
* Miller-Rabin Primality Test implementation in Java (Conceptual).
*/
public class MillerRabin {
private static BigInteger power(BigInteger base, BigInteger exp, BigInteger mod) {
return base.modPow(exp, mod);
}
private static boolean millerTest(BigInteger d, BigInteger n) {
// Simplified base 'a', real version uses randomness or deterministic bases
BigInteger a = new BigInteger("2");
BigInteger x = power(a, d, n);
if (x.equals(BigInteger.ONE) || x.equals(n.subtract(BigInteger.ONE))) return true;
BigInteger two = BigInteger.valueOf(2);
while (d.compareTo(n.subtract(BigInteger.ONE)) < 0) {
x = power(x, two, n);
d = d.multiply(two);
if (x.equals(BigInteger.ONE)) return false;
if (x.equals(n.subtract(BigInteger.ONE))) return true;
}
return false;
}
public static boolean isPrime(long n, int k) {
BigInteger num = BigInteger.valueOf(n);
if (num.compareTo(BigInteger.valueOf(2)) < 0) return false;
if (num.equals(BigInteger.valueOf(2)) || num.equals(BigInteger.valueOf(3))) return true;
if (num.mod(BigInteger.valueOf(2)).equals(BigInteger.ZERO)) return false;
BigInteger d = num.subtract(BigInteger.ONE);
while (d.mod(BigInteger.valueOf(2)).equals(BigInteger.ZERO)) {
d = d.divide(BigInteger.valueOf(2));
}
for (int i = 0; i < k; i++) {
if (!millerTest(d, num)) return false;
}
return true;
}
}# PRESENTING CODE
6. Miller-Rabin Primality Test
Description: A probabilistic primality test to quickly determine if a large number $n$ is prime.
Data Structure Operations
import java.util.ArrayDeque;
import java.util.Deque;
/**
* Stack implementation (LIFO) using ArrayDeque.
*/
public class StackOperations {
private Deque<Integer> stack = new ArrayDeque<>();
public void push(int item) { stack.push(item); }
public int pop() { return stack.pop(); }
public int peek() { return stack.peek(); }
public boolean isEmpty() { return stack.isEmpty(); }
}# PRESENTING CODE
1. Stack (Essential)
Description: Represents a Last-In, First-Out (LIFO) structure.
import java.util.ArrayDeque;
import java.util.Queue;
/**
* Queue implementation (FIFO) using ArrayDeque.
*/
public class QueueOperations {
private Queue<String> queue = new ArrayDeque<>();
public void enqueue(String item) { queue.add(item); }
public String dequeue() { return queue.remove(); }
public String peek() { return queue.peek(); }
public boolean isEmpty() { return queue.isEmpty(); }
}# PRESENTING CODE
2. Queue (Essential)
Description: Represents a First-In, First-Out (FIFO) structure.
/**
* Singly Linked List implementation in Java.
*/
public class LinkedListOperations {
private class Node {
int data;
Node next;
public Node(int data) { this.data = data; this.next = null; }
}
private Node head;
public void insertAtHead(int data) {
Node newNode = new Node(data);
newNode.next = head;
head = newNode;
}
public boolean search(int data) {
Node current = head;
while (current != null) {
if (current.data == data) return true;
current = current.next;
}
return false;
}
}# PRESENTING CODE
3. Linked List (Essential)
Description: A sequence of nodes where each node points to the next.
/**
* Binary Search Tree (BST) implementation in Java.
*/
public class BSTOperations {
private class Node {
int key;
Node left, right;
public Node(int item) { key = item; left = right = null; }
}
private Node root;
public boolean search(int key) {
return searchRec(root, key);
}
private boolean searchRec(Node root, int key) {
if (root == null) return false;
if (root.key == key) return true;
if (root.key > key) {
return searchRec(root.left, key);
} else {
return searchRec(root.right, key);
}
}
}# PRESENTING CODE
4. Binary Search Tree (BST) (Essential)
Description: Left child < Root < Right child. Allows for quick lookups.
/**
* Binary Search Tree (BST) implementation in Java.
*/
public class BSTOperations {
private class Node {
int key;
Node left, right;
public Node(int item) { key = item; left = right = null; }
}
private Node root;
public boolean search(int key) {
return searchRec(root, key);
}
private boolean searchRec(Node root, int key) {
if (root == null) return false;
if (root.key == key) return true;
if (root.key > key) {
return searchRec(root.left, key);
} else {
return searchRec(root.right, key);
}
}
}# PRESENTING CODE
5. Heap (Essential)
Description: Specialized tree satisfying the heap property (e.g., Min Heap: Parent <= Children).
import java.util.HashMap;
import java.util.Map;
/**
* Hash Map implementation in Java.
*/
public class HashMapOperations {
private Map<String, Integer> map = new HashMap<>();
public void insert(String key, int value) { map.put(key, value); }
public Integer search(String key) { return map.get(key); }
public void delete(String key) { map.remove(key); }
public boolean containsKey(String key) { return map.containsKey(key); }
}# PRESENTING CODE
6. Hash Map (Essential)
Description: Stores key-value pairs using a hash function, providing average $O(1)$ access time.
/**
* Disjoint Set Union (DSU) or Union-Find implementation.
*/
public class DisjointSet {
private int[] parent;
private int[] size;
public DisjointSet(int n) {
parent = new int[n];
size = new int[n];
for (int i = 0; i < n; i++) { parent[i] = i; size[i] = 1; }
}
public int find(int i) {
if (parent[i] == i) return i;
return parent[i] = find(parent[i]);
}
public boolean union(int i, int j) {
int rootI = find(i);
int rootJ = find(j);
if (rootI == rootJ) return false;
if (size[rootI] < size[rootJ]) {
parent[rootI] = rootJ;
size[rootJ] += size[rootI];
} else {
parent[rootJ] = rootI;
size[rootI] += size[rootJ];
}
return true;
}
}# PRESENTING CODE
7. Disjoint Set (Union-Find) (Essential)
Description: Manages partitioned sets, supporting Find (root of set) and Union (merge sets).
Utility / Miscellaneous
/**
* Kadane's Algorithm implementation in Java.
*/
public class KadanesAlgorithm {
public static int maxSubarraySum(int[] arr) {
if (arr.length == 0) return 0;
int max_so_far = arr[0];
int max_ending_here = arr[0];
for (int i = 1; i < arr.length; i++) {
max_ending_here = Math.max(arr[i], max_ending_here + arr[i]);
max_so_far = Math.max(max_so_far, max_ending_here);
}
return max_so_far;
}
}# PRESENTING CODE
1. Kadane's Algorithm (Essential)
Description: Finds the maximum contiguous subarray sum in linear time.
/**
* Sliding Window implementation in Java (Fixed size window example).
*/
public class SlidingWindow {
public static int findMaxSumSubarray(int[] arr, int k) {
if (k > arr.length) return -1;
int currentWindowSum = 0;
for (int i = 0; i < k; i++) {
currentWindowSum += arr[i];
}
int maxSum = currentWindowSum;
for (int i = k; i < arr.length; i++) {
currentWindowSum += arr[i] - arr[i - k];
maxSum = Math.max(maxSum, currentWindowSum);
}
return maxSum;
}
}# PRESENTING CODE
2. Sliding Window (Essential)
Description: Technique to analyze contiguous subarrays/subsequences efficiently by maintaining a window.
import java.util.Random;
/**
* Reservoir Sampling implementation in Java.
*/
public class ReservoirSampling {
public static int[] sample(int[] stream, int k) {
int[] reservoir = new int[k];
Random rand = new Random();
for (int i = 0; i < k; i++) {
reservoir[i] = stream[i];
}
for (int i = k; i < stream.length; i++) {
int j = rand.nextInt(i + 1);
if (j < k) {
reservoir[j] = stream[i];
}
}
return reservoir;
}
}# PRESENTING CODE
3. Reservoir Sampling
Description: Randomly samples k items from a stream of unknown length (n) in a single pass.
/**
* Binary Exponentiation (Exponentiation by Squaring) implementation (Iterative).
*/
public class BinaryExponentiation {
public static long power(long base, int exponent) {
long result = 1;
if (exponent < 0) throw new IllegalArgumentException("Exponent must be non-negative.");
if (base == 0 && exponent == 0) return 1;
while (exponent > 0) {
if ((exponent & 1) == 1) {
result *= base;
}
exponent >>= 1;
base *= base;
}
return result;
}
}# PRESENTING CODE
4. Binary Exponentiation (Exponentiation by Squaring)
Description: Computes large positive integer powers efficiently by using the binary representation of the exponent.
Code
By Irving Norehem Llamas Covarrubias
Code
- 25