Java for DSA — Control Flow
3. Control Flow
Why this matters for DSA Control flow defines the logic structure of algorithms. In DSA, loop conditions directly control space/time execution. Most optimal patterns (like Two-Pointers, Binary Search, and BFS queue traversal) are structured around carefully crafted loop and conditional skeletons.
3.1 If-Else & Ternary Operator
Conditionals dictate the direction of your logic.
int x = 10;
// 1. If-Else-If Chain
if (x > 0) {
System.out.println("Positive");
} else if (x < 0) {
System.out.println("Negative");
} else {
System.out.println("Zero");
}
// 2. Ternary Operator (Inline representation of If-Else)
// Syntax: (condition) ? value_if_true : value_if_false;
String result = (x % 2 == 0) ? "Even" : "Odd";
Floating-Point Comparison Guard
When comparing double/float values, direct comparison == can fail due to precision errors. Always check if the absolute difference is within a tiny tolerance ():
double a = 0.1 + 0.2;
double b = 0.3;
if (Math.abs(a - b) < 1e-9) {
// Treat as equal
}
3.2 Switch Expressions (Modern Java)
When branching on a single variable with multiple discrete values (like menu options, state machine changes), a switch is cleaner than long if-else chains.
int day = 3;
// Traditional Switch
switch (day) {
case 1:
System.out.println("Monday");
break;
case 2:
System.out.println("Tuesday");
break;
default:
System.out.println("Other day");
}
// Modern Switch Expression (Java 14+ - arrow syntax)
// No break statements needed! Prevents fall-through bugs.
String dayName = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
default -> "Other day";
};
3.3 For Loops & Enhanced For-Each
For loops are used when the number of iterations is known beforehand.
1. Traditional for (with Index)
// Forward iteration
for (int i = 0; i < 5; i++) {
System.out.print(i + " "); // 0 1 2 3 4
}
// Reverse iteration (Extremely common in DP, array modifications)
for (int i = n - 1; i >= 0; i--) {
// ...
}
2. Enhanced For-Each Loop
If you do not need the index of elements, use the enhanced loop. It is cleaner and prevents off-by-one errors.
int[] nums = {10, 20, 30};
for (int val : nums) {
System.out.println(val);
}
3. Nested Loops (2D Matrix Traversal)
int[][] matrix = {
{1, 2},
{3, 4}
};
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
// Access matrix[i][j]
}
}
3.4 While & Do-While Loops
While loops are preferred when the termination condition changes dynamically during execution (like queue size, shrinking intervals, pointer convergence).
int count = 0;
while (count < 5) {
System.out.println(count);
count++;
}
// Do-While (guaranteed to execute at least once)
int val = 10;
do {
System.out.println(val); // prints 10 once, then checks condition
val++;
} while (val < 5);
Essential DSA While-Loop Skeletons
// 1. Two-Pointers (Converging from ends)
int left = 0, right = arr.length - 1;
while (left < right) {
// Logic (e.g. check sum, swap elements)
left++;
right--;
}
// 2. Binary Search (Dynamic range reduction)
int low = 0, high = arr.length - 1;
while (low <= high) {
int mid = low + (high - low) / 2; // avoids overflow
if (arr[mid] == target) {
// found
} else if (arr[mid] < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
// 3. BFS (Queue traversal)
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
Node curr = queue.poll();
// Traverse children
}
}
3.5 Loop Control: Break & Continue
// break: terminates the loop immediately
for (int val : nums) {
if (val == target) {
break; // Stop scanning once target is found
}
}
// continue: skips the rest of the current iteration and jumps to the next evaluation
for (int i = 1; i <= 5; i++) {
if (i % 2 == 0) {
continue; // Skip even numbers
}
System.out.print(i + " "); // 1 3 5
}
Labelled Break / Continue
In nested loops, break only exits the innermost loop. Java allows you to define labels to break out of outer loops directly — very helpful in grid search algorithms:
outerLoop:
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (grid[r][c] == target) {
System.out.println("Found at: " + r + "," + c);
break outerLoop; // Exits both loops!
}
}
}
Practice Drill
// Try implementing these:
// 1. Print all numbers from 1 to 30 that are divisible by both 3 and 5.
// 2. Use a while loop with two pointers to check if a sorted array contains two numbers that sum to a target.
// 3. Use nested loops to print a triangle pattern:
// *
// **
// ***
// ****
// 4. Find the first index of a target value in a 2D matrix using a labelled break.
💡 Click for Solutions
public class ControlFlowDrill {
public static void main(String[] args) {
// 1. Divisible by 3 and 5
System.out.print("Divisible by 3 and 5: ");
for (int i = 1; i <= 30; i++) {
if (i % 3 == 0 && i % 5 == 0) {
System.out.print(i + " "); // 15 30
}
}
System.out.println();
// 2. Two sum in sorted array
int[] sortedArr = {1, 3, 5, 8, 12, 15};
int targetSum = 13;
boolean hasSum = checkTwoSum(sortedArr, targetSum);
System.out.println("Has target sum: " + hasSum); // true (5 + 8 = 13)
// 3. Triangle pattern
int rows = 4;
for (int i = 1; i <= rows; i++) {
for (int j = 1; j <= i; j++) {
System.out.print("*");
}
System.out.println();
}
// 4. Find in 2D matrix with label
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int targetVal = 5;
int foundRow = -1, foundCol = -1;
searchLabel:
for (int r = 0; r < matrix.length; r++) {
for (int c = 0; c < matrix[r].length; c++) {
if (matrix[r][c] == targetVal) {
foundRow = r;
foundCol = c;
break searchLabel;
}
}
}
System.out.printf("Target found at row %d, col %d\n", foundRow, foundCol); // 1, 1
}
public static boolean checkTwoSum(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;
while (left < right) {
int currentSum = arr[left] + arr[right];
if (currentSum == target) {
return true;
} else if (currentSum < target) {
left++;
} else {
right--;
}
}
return false;
}
}