Sorting Algorithms in Java: Basics and Examples. Practical Tasks
Nine hands-on exercises reinforce the algorithms from the course: sorting, computing an average, Fibonacci numbers, estimating complexity with Big O notation, swapping values, reversing an array, and searching for an element. Each task builds on a specific lesson and asks you to step through code in the debugger, fill in a trace table, or refine an implementation.
How to work through the trace tasks
Set a breakpoint on the line that swaps elements and run the program in Debug mode. Use Step Over to walk through the loop, and on every iteration record the counter values and the array state in a table — that turns the algorithm into something you can see, instead of a «black box».
1. Debugging Bubble Sort
- Create a table for any array where you log the values of
i,j, and the current array state for each cycle of the Bubble Sort algorithm. - Use the debugger in your IDE.
- For example, for the array 0 2 5 3 4:
Debugging Steps i j Array State Did if block run? 0 4 0 2 5 3 4 - 0 3 0 2 3 5 4 + 0 2 0 2 3 5 4 - 0 1 0 2 3 5 4 - 1 4 0 2 3 4 5 + 1 3 0 2 3 4 5 - 1 2 0 2 3 4 5 - 2 4 0 2 3 4 5 - 2 3 0 2 3 4 5 - 3 4 0 2 3 4 5 - 4 - 0 2 3 4 5 -
2. Modify the Bubble Sort
- Update the Bubble Sort program:
a) Add early termination if no swaps were made in a pass.
b) The current implementation "floats" the minimum element to the start of the array. Modify the program so that it pushes the minimum element to the end of the array (i.e., the innerforloop should iterate from start to end).public class BubbleSorter { public static void sort(int[] array) { for (int i = 0; i < array.length; i++) { for (int j = array.length - 1; j > i; j--) { if (array[j - 1] > array[j]) { int tmp = array[j - 1]; array[j - 1] = array[j]; array[j] = tmp; } } } } }
3. Debugging Selection Sort
Repeat Task 1 using the Selection Sort algorithm.
- Modify the Selection Sort – exclude the swap operation if the minimum element is already in the correct position.
public class SelectionSorter { public static void sort(int[] array) { for (int i = 0; i < array.length; i++) { int pos = i; int min = array[i]; for (int j = i + 1; j < array.length; j++) { if (array[j] < min) { pos = j; min = array[j]; } } if (pos != i) { array[pos] = array[i]; array[i] = min; } } } }
4. Find the Bug in an Average Calculation
Given the following method that is supposed to calculate the average of an int[] array:
public class AverageCalculator {
public static double average(int[] array) {
int sum = 0;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
int count = array.length;
return sum / count;
}
} - Run the method for the array {1, 2, 3, 4}. The expected result is 2.5, but the program returns 2. Use the debugger to find the cause by checking the result of
sum / countbefore it is assigned to adoublevariable. - Fix the bug using the integer division section of the «Average Value of an Array in Java» lesson.
- Add a check for an empty array (
array.length == 0): the method should return 0 instead of throwing anArithmeticException.
5. Compare Recursion and Iteration for Fibonacci Numbers
- Take the recursive method and the loop-based method for computing Fibonacci numbers from the «Fibonacci Numbers in Java» lesson.
- Measure the execution time (using
System.currentTimeMillis()before and after the call) for n = 30, n = 35, and n = 40 for both implementations. Record the results in a table.Execution time, ms n Recursion Loop 30 35 40 - Explain the result: why does the recursive implementation slow down dramatically as
ngrows? Connect your answer to the O(2^n) versus O(n) complexity covered in the lesson. - Add memoization to the recursive method (cache already computed values in a
HashMap<Integer, Long>) and repeat the measurement for n = 40.
6. Determine Algorithm Complexity from Code
For each method below, determine its Big O complexity based on the «Algorithm Complexity and Big O Notation» lesson, and justify your answer in one sentence.
// A
public static int first(int[] array) {
return array[0];
}
// B
public static int sum(int[] array) {
int s = 0;
for (int x : array) {
s += x;
}
return s;
}
// C
public static boolean hasDuplicate(int[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = i + 1; j < array.length; j++) {
if (array[i] == array[j]) {
return true;
}
}
}
return false;
}
// D
public static int binarySearch(int[] array, int target) {
int low = 0;
int high = array.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (array[mid] == target) {
return mid;
} else if (array[mid] < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
} - Fill in the table.
Complexity of methods A–D Method Big O A B C D - Check yourself: measure the execution time of the
hasDuplicatemethod for arrays of 1,000, 10,000, and 100,000 elements and confirm that the growth matches your complexity estimate.
7. Prove That Swap Does Not Work on Primitives
- Write the
swap(int a, int b)method from the «How to Swap Variables in Java» lesson and call it on two local variables. Use the debugger (or print statements before and after the call) to confirm that the caller's variables did not change. - Explain why, based on how Java passes primitive parameters: by value, not by reference.
- Write a
swap(int[] array, int i, int j)method that swaps two array elements by index, and prove that this time the swap works: the reference parameter points to the same array, and it is the array's elements that change, not the parameter itself. - Use your
swapmethod for arrays inside your own Bubble Sort or Selection Sort implementation instead of a manual temporary-variable swap.
8. Trace the Array Reversal Algorithm
- Take the in-place array reversal method (two pointers,
leftandright) from the «Reverse an Array» lesson. - For the array {1, 2, 3, 4, 5}, fill in a step table like in Task 1, recording
left,right, and the array state on every loop iteration.Array reversal steps left right Array State - Use the debugger to check how many loop iterations were needed for an array of 5 elements versus 6 elements, and explain the difference between even and odd array lengths.
- Write a version of the method that does not mutate the original array but returns a new reversed array, and compare its space complexity with the in-place version.
9. Compare Linear and Binary Search
- Take the linear search and binary search implementations from the «Search Algorithms» lesson.
- Add a comparison counter (
int comparisons) to both methods and print its value after the search finishes. - Create a sorted array of 100 elements and search for: the first element, the last element, and an element that is not in the array. Fill in the table with the number of comparisons for each case.
Number of comparisons Target Linear Search Binary Search First element Last element Missing element - Explain why the number of comparisons in binary search barely changes as the array grows, while in linear search it grows proportionally. Connect your answer to the «Algorithm Complexity and Big O Notation» lesson.
Frequently Asked Questions
Why does the method in Task 4 return 2 instead of 2.5?
Both operands, sum and count, are of type int, so sum / count is evaluated as integer division before the result is assigned to a double variable, and the fractional part is discarded. To get 2.5, cast at least one operand to a floating-point type: (double) sum / count.
Why does swap(int a, int b) leave the original variables unchanged?
Java always passes arguments by value. The method receives copies of the variables' values, so the exchange happens only inside the method and never affects the caller's variables. Swapping array elements works because a copy of the reference to the same array is passed, and it is the array's elements that change.
Why does the recursive Fibonacci slow down so sharply?
Naive recursion recomputes the same values many times, and the number of calls grows exponentially, giving O(2 to the power of n) complexity. The loop-based version is linear, O(n). Memoization, that is caching already computed values, brings the recursive version down to O(n).
How does the complexity of linear search differ from binary search?
Linear search scans every element in the worst case, so its complexity is O(n). Binary search discards half of the range at each step and runs in O(log n), but it requires a sorted array first.
Comments