diff --git a/src/main/java/com/thealgorithms/sorts/TagSort.java b/src/main/java/com/thealgorithms/sorts/TagSort.java new file mode 100644 index 000000000000..2f7d890eb6a9 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/TagSort.java @@ -0,0 +1,72 @@ +package com.thealgorithms.sorts; + +import java.util.Arrays; +import java.util.Comparator; + +/** + * Tag Sort (also known as Index Sort or Dictionary Sort). + * + *

Tag Sort is a sorting technique that does not modify the original array. + * Instead, it creates an array of indices (tags) and sorts those indices based + * on the corresponding values in the original array. This is useful when you + * need to know the original positions of elements after sorting. + * + *

Time Complexity: O(n log n) + * Space Complexity: O(n) + * + * @see Tag Sort - GeeksForGeeks + */ +public class TagSort implements SortAlgorithm { + + /** + * Sorts the given array using Tag Sort. + * Returns the sorted array (copy), leaving the original array unchanged. + * + * @param array the array to be sorted + * @param the type of elements, must be Comparable + * @return a new sorted array + */ + @Override + public > T[] sort(final T[] array) { + if (array.length == 0) { + return array; + } + + Integer[] tags = createTags(array); + sortTags(tags, array); + + @SuppressWarnings("unchecked") + T[] sortedArray = (T[]) new Comparable[array.length]; + for (int i = 0; i < array.length; i++) { + sortedArray[i] = array[tags[i]]; + } + + System.arraycopy(sortedArray, 0, array, 0, array.length); + return array; + } + + /** + * Returns the sorted indices (tags) of the given array without modifying it. + * + * @param array the input array + * @param the type of elements, must be Comparable + * @return an array of indices representing the sorted order + */ + public > Integer[] getSortedTags(final T[] array) { + Integer[] tags = createTags(array); + sortTags(tags, array); + return tags; + } + + private > Integer[] createTags(final T[] array) { + Integer[] tags = new Integer[array.length]; + for (int i = 0; i < array.length; i++) { + tags[i] = i; + } + return tags; + } + + private > void sortTags(final Integer[] tags, final T[] array) { + Arrays.sort(tags, Comparator.comparing(i -> array[i])); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/TagSortTest.java b/src/test/java/com/thealgorithms/sorts/TagSortTest.java new file mode 100644 index 000000000000..f66ad46dcc53 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/TagSortTest.java @@ -0,0 +1,98 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TagSortTest { + + private TagSort tagSort; + + @BeforeEach + public void setUp() { + tagSort = new TagSort(); + } + + @Test + @DisplayName("TagSort empty array") + public void testEmptyArray() { + Integer[] input = {}; + Integer[] result = tagSort.sort(input); + assertArrayEquals(new Integer[] {}, result); + } + + @Test + @DisplayName("TagSort single element array") + public void testSingleElement() { + Integer[] input = {42}; + Integer[] result = tagSort.sort(input); + assertArrayEquals(new Integer[] {42}, result); + } + + @Test + @DisplayName("TagSort non-duplicate integer array") + public void testNonDuplicateIntegers() { + Integer[] input = {5, 3, 8, 1, 9, 2}; + Integer[] result = tagSort.sort(input); + assertArrayEquals(new Integer[] {1, 2, 3, 5, 8, 9}, result); + } + + @Test + @DisplayName("TagSort integer array with duplicates") + public void testDuplicateIntegers() { + Integer[] input = {4, 2, 7, 2, 4, 1}; + Integer[] result = tagSort.sort(input); + assertArrayEquals(new Integer[] {1, 2, 2, 4, 4, 7}, result); + } + + @Test + @DisplayName("TagSort negative integers") + public void testNegativeIntegers() { + Integer[] input = {-3, 5, -1, 0, 2, -8}; + Integer[] result = tagSort.sort(input); + assertArrayEquals(new Integer[] {-8, -3, -1, 0, 2, 5}, result); + } + + @Test + @DisplayName("TagSort already sorted array") + public void testAlreadySorted() { + Integer[] input = {1, 2, 3, 4, 5}; + Integer[] result = tagSort.sort(input); + assertArrayEquals(new Integer[] {1, 2, 3, 4, 5}, result); + } + + @Test + @DisplayName("TagSort reverse sorted array") + public void testReverseSorted() { + Integer[] input = {5, 4, 3, 2, 1}; + Integer[] result = tagSort.sort(input); + assertArrayEquals(new Integer[] {1, 2, 3, 4, 5}, result); + } + + @Test + @DisplayName("TagSort string array") + public void testStringArray() { + String[] input = {"banana", "apple", "cherry", "date"}; + String[] result = tagSort.sort(input); + assertArrayEquals(new String[] {"apple", "banana", "cherry", "date"}, result); + } + + @Test + @DisplayName("TagSort getSortedTags returns correct indices") + public void testGetSortedTags() { + Integer[] input = {30, 10, 20}; + Integer[] tags = tagSort.getSortedTags(input); + // index 1 (value 10), index 2 (value 20), index 0 (value 30) + assertArrayEquals(new Integer[] {1, 2, 0}, tags); + } + + @Test + @DisplayName("TagSort all equal elements") + public void testAllEqualElements() { + Integer[] input = {7, 7, 7, 7}; + Integer[] result = tagSort.sort(input); + assertArrayEquals(new Integer[] {7, 7, 7, 7}, result); + } +}