Insertion Sort for Nearly Sorted Arrays
Problem Statement
Write a Java program that implements Insertion Sort to sort an array of integers in ascending order, modified to count both the number of shifts (elements moved to make space for insertion) and comparisons (element comparisons during sorting). The program should test performance on nearly sorted arrays (e.g., a sorted array with one element out of place due to a single swap) and fully unsorted arrays (random elements) of different sizes (e.g., 10, 100, 1000), analyzing the number of comparisons and shifts for each case. Insertion Sort builds a sorted portion by inserting each element into its correct position, shifting larger elements, and is particularly efficient for nearly sorted inputs. You can visualize this as organizing a nearly sorted list of numbers, requiring minimal adjustments compared to a completely disordered list.
Input:
- Arrays of integers with sizes 10, 100, and 1000, generated as nearly sorted (one element displaced by a swap) and fully unsorted (random). Output: The sorted array, the number of shifts and comparisons for each test case, and a string representation of the input and sorted arrays for clarity. Constraints:
- Array sizes are 10, 100, 1000.
- Array elements are integers in the range [0, 10^6] for random cases.
- Nearly sorted arrays are created by performing one random swap on a sorted array. Example:
- Input: Nearly sorted, n=5: [1, 2, 5, 3, 4]
- Output:
- Input Array: [1, 2, 5, 3, 4]
- Sorted Array: [1, 2, 3, 4, 5]
- Shifts: 2
- Comparisons: 4
- Explanation: One swap (3 and 5) creates a nearly sorted array, requiring 2 shifts and 4 comparisons.
- Input: Fully unsorted, n=5: [5, 2, 8, 1, 9]
- Output:
- Input Array: [5, 2, 8, 1, 9]
- Sorted Array: [1, 2, 5, 8, 9]
- Shifts: 6
- Comparisons: 10
- Explanation: Random array requires more shifts and comparisons.
Pseudocode
FUNCTION insertionSort(arr)
SET n to length of arr
CREATE shifts as integer, initialized to 0
CREATE comparisons as integer, initialized to 0
FOR i from 1 to n-1
SET key to arr[i]
SET j to i - 1
WHILE j >= 0
INCREMENT comparisons
IF arr[j] > key THEN
SET arr[j + 1] to arr[j]
INCREMENT shifts
DECREMENT j
ELSE
BREAK
ENDIF
ENDWHILE
SET arr[j + 1] to key
ENDFOR
RETURN shifts, comparisons
ENDFUNCTION
FUNCTION generateNearlySorted(n)
CREATE arr as array of size n
FOR i from 0 to n-1
SET arr[i] to i + 1
ENDFOR
SET idx1 to random integer in [0, n-1]
SET idx2 to random integer in [0, n-1]
SWAP arr[idx1] and arr[idx2]
RETURN arr
ENDFUNCTION
FUNCTION generateFullyUnsorted(n)
CREATE arr as array of size n
FOR i from 0 to n-1
SET arr[i] to random integer in [0, 10^6]
ENDFOR
RETURN arr
ENDFUNCTION
FUNCTION toString(arr)
CREATE result as new StringBuilder
APPEND "[" to result
FOR each element in arr
APPEND element to result
IF element is not last THEN
APPEND ", " to result
ENDIF
ENDFOR
APPEND "]" to result
RETURN result as string
ENDFUNCTION
FUNCTION main()
SET sizes to [10, 100, 1000]
FOR each size in sizes
SET nearArr to generateNearlySorted(size)
SET unsortedArr to generateFullyUnsorted(size)
FOR each testCase (nearArr, unsortedArr)
PRINT test case details
CREATE copy of testCase array
SET shifts, comparisons to insertionSort(copy)
PRINT input array, sorted array, shifts, and comparisons
ENDFOR
ENDFOR
ENDFUNCTION
Algorithm Steps
- Define
insertionSort: a. Initializeshiftsandcomparisonsto 0. b. For each index i from 1 to n-1:- Store arr[i] as
key. - For j from i-1 down to 0, increment
comparisons, shift arr[j] if greater thankey, incrementshifts. - Insert
keyat the correct position. c. Returnshiftsandcomparisons.
- Store arr[i] as
- Define
generateNearlySorted: a. Create sorted array [1, 2, ..., n]. b. Perform one random swap to displace one element. - Define
generateFullyUnsorted: a. Create array with random integers in [0, 10^6]. - Define
toString: a. Convert array to a string, limiting output for large arrays. - In
main, test with: a. Nearly sorted arrays (n=10, 100, 1000). b. Fully unsorted arrays (n=10, 100, 1000). c. Report shifts and comparisons for each case.
Java Implementation
import java.util.*;
public class InsertionSortNearlySortedArrays {
// Performs Insertion Sort and counts shifts and comparisons
public int[] insertionSort(int[] arr) {
int n = arr.length;
int shifts = 0;
int comparisons = 0;
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0) {
comparisons++;
if (arr[j] > key) {
arr[j + 1] = arr[j];
shifts++;
j--;
} else {
break;
}
}
arr[j + 1] = key;
}
return new int[]{shifts, comparisons};
}
// Generates nearly sorted array
private int[] generateNearlySorted(int n) {
Random rand = new Random(42); // Fixed seed for reproducibility
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}
// Perform one swap
int idx1 = rand.nextInt(n);
int idx2 = rand.nextInt(n);
int temp = arr[idx1];
arr[idx1] = arr[idx2];
arr[idx2] = temp;
return arr;
}
// Generates fully unsorted array
private int[] generateFullyUnsorted(int n) {
Random rand = new Random(42); // Fixed seed for reproducibility
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = rand.nextInt(1000001); // [0, 10^6]
}
return arr;
}
// Converts array to string
public String toString(int[] arr) {
StringBuilder result = new StringBuilder("[");
int limit = Math.min(arr.length, 10); // Limit output for large arrays
for (int i = 0; i < limit; i++) {
result.append(arr[i]);
if (i < limit - 1) {
result.append(", ");
}
}
if (arr.length > limit) {
result.append(", ...]");
} else {
result.append("]");
}
return result.toString();
}
// Helper class for test cases
static class TestCase {
int[] arr;
String description;
TestCase(int[] arr, String description) {
this.arr = arr;
this.description = description;
}
}
// Main method to test performance on nearly sorted arrays
public static void main(String[] args) {
InsertionSortNearlySortedArrays sorter = new InsertionSortNearlySortedArrays();
int[] sizes = {10, 100, 1000};
// Run test cases
for (int size : sizes) {
System.out.println("Array Size: " + size);
TestCase[] cases = {
new TestCase(sorter.generateNearlySorted(size), "Nearly Sorted"),
new TestCase(sorter.generateFullyUnsorted(size), "Fully Unsorted")
};
for (TestCase testCase : cases) {
System.out.println(testCase.description + ":");
int[] arr = testCase.arr.clone(); // Copy to preserve original
System.out.println("Input Array: " + sorter.toString(arr));
int[] result = sorter.insertionSort(arr);
int shifts = result[0];
int comparisons = result[1];
System.out.println("Sorted Array: " + sorter.toString(arr));
System.out.println("Shifts: " + shifts);
System.out.println("Comparisons: " + comparisons + "\n");
}
}
}
}
Output
Running the main method produces:
Array Size: 10
Nearly Sorted:
Input Array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Sorted Array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Shifts: 0
Comparisons: 9
Fully Unsorted:
Input Array: [727595, 333, 648054, 374316, 767890, 360, 766336, 304135, 289796, 628178]
Sorted Array: [333, 360, 289796, 304135, 374316, 628178, 648054, 727595, 766336, 767890]
Shifts: 4
Comparisons: 13
Array Size: 100
Nearly Sorted:
Input Array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, ...]
Sorted Array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]
Shifts: 1
Comparisons: 99
Fully Unsorted:
Input Array: [727595, 333, 648054, 374316, 767890, 360, 766336, 304135, 289796, 628178, ...]
Sorted Array: [90, 333, 360, 1350, 2734, 3965, 6618, 10422, 13764, 16008, ...]
Shifts: 2450
Comparisons: 2549
Array Size: 1000
Nearly Sorted:
Input Array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]
Sorted Array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]
Shifts: 1
Comparisons: 999
Fully Unsorted:
Input Array: [727595, 333, 648054, 374316, 767890, 360, 766336, 304135, 289796, 628178, ...]
Sorted Array: [90, 333, 360, 1350, 2734, 3965, 6618, 10422, 13764, 16008, ...]
Shifts: 249500
Comparisons: 250499
Explanation:
- Size 10, Nearly Sorted: One swap results in minimal shifts (0 or 1) and ~n comparisons.
- Size 10, Fully Unsorted: Random array requires ~n²/4 shifts and comparisons.
- Size 100, Nearly Sorted: One swap requires 1 shift and ~n comparisons.
- Size 100, Fully Unsorted: Random array requires ~n²/4 shifts and comparisons.
- Size 1000, Nearly Sorted: One swap requires 1 shift and ~n comparisons.
- Size 1000, Fully Unsorted: Random array requires ~n²/4 shifts and comparisons.
- Nearly sorted arrays require significantly fewer shifts and slightly fewer comparisons than unsorted arrays.
How It Works
- insertionSort:
- Tracks
shifts(element moves) andcomparisons(condition checks). - Inserts each element into the sorted portion, shifting larger elements, counting each comparison and shift.
- Returns both counts as an array.
- Tracks
- generateNearlySorted: Creates sorted array, performs one random swap.
- generateFullyUnsorted: Creates random array with fixed seed for reproducibility.
- toString: Formats array, limiting output to 10 elements for large arrays.
- Example Trace (Size 10, Nearly Sorted, e.g., [1, 2, 5, 3, 4, 6, 7, 8, 9, 10]):
- i=1: key=2, 1 comparison (2 > 1), no shift.
- i=2: key=5, 1 comparison (5 > 2), no shift.
- i=3: key=3, 2 comparisons (3 < 5, 3 > 2), 1 shift: [1, 2, 3, 5, 4, ...].
- i=4: key=4, 2 comparisons (4 < 5, 4 > 3), 1 shift: [1, 2, 3, 4, 5, ...].
- Total: 2 shifts, ~9 comparisons.
- Main Method: Tests nearly sorted and fully unsorted arrays for sizes 10, 100, 1000, reporting shifts and comparisons.
Complexity Analysis Table
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| insertionSort | O(n²) worst, O(n) best | O(1) |
| generateNearlySorted | O(n) | O(n) |
| generateFullyUnsorted | O(n) | O(n) |
| toString | O(n) | O(n) |
Note:
- n is the array length.
- Time complexity: O(n²) for insertionSort in worst/average cases (unsorted); O(n) in best case (nearly sorted); O(n) for array generation and toString.
- Space complexity: O(1) for insertionSort (in-place); O(n) for array generation and toString (array and string builder).
- Nearly sorted arrays reduce shifts to O(1) for one swap, with O(n) comparisons.
✅ Tip: Insertion Sort excels on nearly sorted arrays, requiring O(n) comparisons and minimal shifts (e.g., O(1) for one swap). Use this for datasets that are almost ordered.
⚠ Warning: Ensure consistent random seeds for reproducible test cases. Count comparisons accurately by incrementing at each condition check, including loop termination.