Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

  1. Define insertionSort: a. Initialize shifts and comparisons to 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 than key, increment shifts.
    • Insert key at the correct position. c. Return shifts and comparisons.
  2. Define generateNearlySorted: a. Create sorted array [1, 2, ..., n]. b. Perform one random swap to displace one element.
  3. Define generateFullyUnsorted: a. Create array with random integers in [0, 10^6].
  4. Define toString: a. Convert array to a string, limiting output for large arrays.
  5. 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) and comparisons (condition checks).
    • Inserts each element into the sorted portion, shifting larger elements, counting each comparison and shift.
    • Returns both counts as an array.
  • 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

OperationTime ComplexitySpace Complexity
insertionSortO(n²) worst, O(n) bestO(1)
generateNearlySortedO(n)O(n)
generateFullyUnsortedO(n)O(n)
toStringO(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.