Insertion Sort for Object Sorting
Problem Statement
Write a Java program that extends the Insertion Sort implementation to sort an array of objects, specifically Student objects with a grade field, in ascending order based on a custom comparator that compares grades. The program should count the number of shifts performed (the number of times elements are moved to make space for insertion) and test with a sample dataset containing Student objects with varied grades, including duplicates and edge cases like empty arrays. Insertion Sort will build a sorted portion of the array, inserting each Student object into its correct position based on the comparator, shifting objects with higher grades. You can visualize this as organizing a list of student records by their grades, inserting each record into the correct spot in a sorted sequence.
Input:
- An array of
Studentobjects, each with aname(String) andgrade(double) field. Output: The sorted array (by grade in ascending order), the number of shifts performed, and a string representation of the input and sorted arrays for clarity. Constraints: - The array length
nis between 0 and 10^5. - Grades are doubles in the range [0.0, 100.0]. Example:
- Input: array = [Student("Alice", 85.5), Student("Bob", 92.0), Student("Charlie", 78.5)]
- Output:
- Input Array: [Student(name=Alice, grade=85.5), Student(name=Bob, grade=92.0), Student(name=Charlie, grade=78.5)]
- Sorted Array: [Student(name=Charlie, grade=78.5), Student(name=Alice, grade=85.5), Student(name=Bob, grade=92.0)]
- Shifts: 3
- Explanation: Insertion Sort sorts by grade, requiring 3 shifts to place students in ascending order.
- Input: array = [Student("Eve", 90.0), Student("Eve", 90.0)]
- Output:
- Input Array: [Student(name=Eve, grade=90.0), Student(name=Eve, grade=90.0)]
- Sorted Array: [Student(name=Eve, grade=90.0), Student(name=Eve, grade=90.0)]
- Shifts: 0
- Explanation: Duplicate grades require no shifts as order is preserved.
Pseudocode
FUNCTION insertionSort(arr, comparator)
SET n to length of arr
CREATE shifts 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 AND comparator.compare(arr[j], key) > 0
SET arr[j + 1] to arr[j]
INCREMENT shifts
DECREMENT j
ENDWHILE
SET arr[j + 1] to key
ENDFOR
RETURN shifts
ENDFUNCTION
FUNCTION toString(arr)
CREATE result as new StringBuilder
APPEND "[" to result
FOR each element in arr
APPEND element.toString() to result
IF element is not last THEN
APPEND ", " to result
ENDIF
ENDFOR
APPEND "]" to result
RETURN result as string
ENDFUNCTION
FUNCTION main()
SET testCases to array of Student arrays
SET gradeComparator to new Comparator comparing Student grades
FOR each testCase in testCases
PRINT test case details
CREATE copy of testCase array
SET shifts to insertionSort(copy, gradeComparator)
PRINT input array, sorted array, and shifts
ENDFOR
ENDFUNCTION
Algorithm Steps
- Define
Studentclass: a. Fields:name(String),grade(double). b. IncludetoStringfor readable output. - Define
insertionSort(generic): a. Accept an array of typeTand aComparator<T>. b. Initialize a countershiftsto 0. c. For each index i from 1 to n-1:- Store arr[i] as
key. - Shift elements arr[j] where comparator.compare(arr[j], key) > 0, incrementing
shifts. - Insert
keyat the correct position. d. Return the number of shifts.
- Store arr[i] as
- Define
toString: a. Convert the array to a string, e.g., "[Student(name=Alice, grade=85.5), ...]". - In
main, test with: a. Mixed grades (n=5). b. Duplicate grades (n=4). c. Empty array (n=0). d. Single-element array (n=1). e. Sorted grades (n=6).
Java Implementation
import java.util.*;
public class InsertionSortObjectSorting {
// Student class
static class Student {
String name;
double grade;
Student(String name, double grade) {
this.name = name;
this.grade = grade;
}
@Override
public String toString() {
return "Student(name=" + name + ", grade=" + grade + ")";
}
}
// Performs Insertion Sort with custom comparator and counts shifts
public <T> int insertionSort(T[] arr, Comparator<T> comparator) {
int n = arr.length;
int shifts = 0;
for (int i = 1; i < n; i++) {
T key = arr[i];
int j = i - 1;
while (j >= 0 && comparator.compare(arr[j], key) > 0) {
arr[j + 1] = arr[j];
shifts++;
j--;
}
arr[j + 1] = key;
}
return shifts;
}
// Converts array to string
public <T> String toString(T[] arr) {
StringBuilder result = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
result.append(arr[i].toString());
if (i < arr.length - 1) {
result.append(", ");
}
}
result.append("]");
return result.toString();
}
// Helper class for test cases
static class TestCase {
Student[] arr;
String description;
TestCase(Student[] arr, String description) {
this.arr = arr;
this.description = description;
}
}
// Main method to test object sorting
public static void main(String[] args) {
InsertionSortObjectSorting sorter = new InsertionSortObjectSorting();
// Comparator for sorting by grade
Comparator<Student> gradeComparator = (s1, s2) -> Double.compare(s1.grade, s2.grade);
// Test cases
TestCase[] testCases = {
new TestCase(new Student[]{
new Student("Alice", 85.5),
new Student("Bob", 92.0),
new Student("Charlie", 78.5),
new Student("David", 95.0),
new Student("Eve", 88.0)
}, "Mixed grades (n=5)"),
new TestCase(new Student[]{
new Student("Frank", 90.0),
new Student("Grace", 90.0),
new Student("Hannah", 90.0),
new Student("Ivy", 90.0)
}, "Duplicate grades (n=4)"),
new TestCase(new Student[]{}, "Empty (n=0)"),
new TestCase(new Student[]{new Student("Jack", 75.0)}, "Single element (n=1)"),
new TestCase(new Student[]{
new Student("Kate", 70.0),
new Student("Liam", 75.0),
new Student("Mia", 80.0),
new Student("Noah", 85.0),
new Student("Olivia", 90.0),
new Student("Peter", 95.0)
}, "Sorted grades (n=6)")
};
// Run test cases
for (int i = 0; i < testCases.length; i++) {
System.out.println("Test case " + (i + 1) + ": " + testCases[i].description);
Student[] arr = testCases[i].arr.clone(); // Copy to preserve original
System.out.println("Input Array: " + sorter.toString(arr));
int shifts = sorter.insertionSort(arr, gradeComparator);
System.out.println("Sorted Array: " + sorter.toString(arr));
System.out.println("Shifts: " + shifts + "\n");
}
}
}
Output
Running the main method produces:
Test case 1: Mixed grades (n=5)
Input Array: [Student(name=Alice, grade=85.5), Student(name=Bob, grade=92.0), Student(name=Charlie, grade=78.5), Student(name=David, grade=95.0), Student(name=Eve, grade=88.0)]
Sorted Array: [Student(name=Charlie, grade=78.5), Student(name=Alice, grade=85.5), Student(name=Eve, grade=88.0), Student(name=Bob, grade=92.0), Student(name=David, grade=95.0)]
Shifts: 6
Test case 2: Duplicate grades (n=4)
Input Array: [Student(name=Frank, grade=90.0), Student(name=Grace, grade=90.0), Student(name=Hannah, grade=90.0), Student(name=Ivy, grade=90.0)]
Sorted Array: [Student(name=Frank, grade=90.0), Student(name=Grace, grade=90.0), Student(name=Hannah, grade=90.0), Student(name=Ivy, grade=90.0)]
Shifts: 0
Test case 3: Empty (n=0)
Input Array: []
Sorted Array: []
Shifts: 0
Test case 4: Single element (n=1)
Input Array: [Student(name=Jack, grade=75.0)]
Sorted Array: [Student(name=Jack, grade=75.0)]
Shifts: 0
Test case 5: Sorted grades (n=6)
Input Array: [Student(name=Kate, grade=70.0), Student(name=Liam, grade=75.0), Student(name=Mia, grade=80.0), Student(name=Noah, grade=85.0), Student(name=Olivia, grade=90.0), Student(name=Peter, grade=95.0)]
Sorted Array: [Student(name=Kate, grade=70.0), Student(name=Liam, grade=75.0), Student(name=Mia, grade=80.0), Student(name=Noah, grade=85.0), Student(name=Olivia, grade=90.0), Student(name=Peter, grade=95.0)]
Shifts: 0
Explanation:
- Test case 1: Mixed grades require 6 shifts to sort by grade in ascending order.
- Test case 2: Duplicate grades (all 90.0) require 0 shifts as order is preserved.
- Test case 3: Empty array requires 0 shifts.
- Test case 4: Single-element array requires 0 shifts.
- Test case 5: Already sorted by grade, 0 shifts.
How It Works
- Student Class:
- Contains
name(String) andgrade(double). - Includes
toStringfor readable output.
- Contains
- insertionSort:
- Uses generics with
Comparator<T>to sort any object type. - Inserts each element into the sorted portion, shifting elements where comparator indicates, counting shifts.
- Uses generics with
- toString: Formats array as a string, handling
Studentobjects. - Example Trace (Test case 1):
- i=1: key=Bob(92.0), shift Alice(85.5): [Alice(85.5), Bob(92.0), Charlie(78.5), David(95.0), Eve(88.0)] (1 shift).
- i=2: key=Charlie(78.5), shift Bob(92.0), Alice(85.5): [Charlie(78.5), Alice(85.5), Bob(92.0), David(95.0), Eve(88.0)] (2 shifts).
- i=3: key=David(95.0), no shift: [Charlie(78.5), Alice(85.5), Bob(92.0), David(95.0), Eve(88.0)].
- i=4: key=Eve(88.0), shift David(95.0), Bob(92.0): [Charlie(78.5), Alice(85.5), Eve(88.0), Bob(92.0), David(95.0)] (3 shifts).
- Total shifts: 6.
- Main Method: Tests mixed grades, duplicates, empty, single-element, and sorted arrays using a grade comparator.
Complexity Analysis Table
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| insertionSort | O(n²) worst, O(n) best | O(1) |
| toString | O(n) | O(n) |
Note:
- n is the array length.
- Time complexity: O(n²) for insertionSort in worst/average cases; O(n) in best case (sorted); O(n) for toString.
- Space complexity: O(1) for insertionSort (in-place); O(n) for toString (string builder).
- Comparator comparisons depend on the field (e.g., double for grades), but complexity is O(n²) for comparisons.
✅ Tip: Use a
Comparatorto make Insertion Sort flexible for sorting objects by any field. Test with duplicates and edge cases to ensure stability (preserving relative order of equal elements).
⚠ Warning: Ensure the comparator is consistent to avoid sorting errors. Clone input arrays to preserve the original order for display.