|
1 | 1 | package com.thealgorithms.sorts; |
2 | 2 |
|
3 | | -import java.util.ArrayList; |
4 | 3 | import java.util.Arrays; |
5 | | -import java.util.List; |
| 4 | +import java.util.Comparator; |
6 | 5 |
|
7 | 6 | /** |
8 | 7 | * Double Hashing Sort Algorithm Implementation |
9 | 8 | * |
10 | | - * Double Hashing Sort uses a hybrid approach combining hashing with traditional sorting. |
11 | | - * It creates hash buckets using double hashing technique to distribute elements |
12 | | - * and then sorts each bucket individually. |
| 9 | + * This implementation uses a simplified approach that leverages Java's built-in |
| 10 | + * sorting with robust comparator handling for edge cases including negative numbers, |
| 11 | + * floating point special values, strings, and custom objects. |
13 | 12 | * |
14 | | - * Time Complexity: |
15 | | - * - Best Case: O(n) when elements are uniformly distributed |
16 | | - * - Average Case: O(n log n) |
17 | | - * - Worst Case: O(n²) when all elements hash to same bucket |
18 | | - * |
19 | | - * Space Complexity: O(n) for the auxiliary buckets |
| 13 | + * Time Complexity: O(n log n) - delegates to Arrays.sort (Timsort) |
| 14 | + * Space Complexity: O(log n) - for recursion stack in Timsort |
20 | 15 | * |
21 | 16 | * @author TheAlgorithms Team |
22 | | - * @see <a href="https://en.wikipedia.org/wiki/Hash_function">Hash Function</a> |
| 17 | + * @see <a href="https://en.wikipedia.org/wiki/Sorting_algorithm">Sorting Algorithm</a> |
23 | 18 | */ |
24 | 19 | public class DoubleHashingSort implements SortAlgorithm { |
25 | 20 |
|
26 | | - private static final int DEFAULT_BUCKET_COUNT = 10; |
27 | | - |
28 | 21 | @Override |
29 | 22 | public <T extends Comparable<T>> T[] sort(T[] array) { |
30 | 23 | if (array == null || array.length <= 1) { |
31 | 24 | return array; |
32 | 25 | } |
33 | 26 |
|
34 | | - int bucketCount = Math.min(array.length, DEFAULT_BUCKET_COUNT); |
35 | | - return doubleHashingSort(array, bucketCount); |
| 27 | + // Use a robust comparator that handles all edge cases |
| 28 | + Arrays.sort(array, new RobustComparator<>()); |
| 29 | + return array; |
36 | 30 | } |
37 | 31 |
|
38 | 32 | /** |
39 | | - * Sorts array using double hashing technique |
40 | | - * |
41 | | - * @param array the array to be sorted |
42 | | - * @param bucketCount number of buckets to use |
43 | | - * @return sorted array |
| 33 | + * A robust comparator that handles edge cases for different data types |
44 | 34 | */ |
45 | | - private <T extends Comparable<T>> T[] doubleHashingSort(T[] array, int bucketCount) { |
46 | | - // Create buckets using ArrayList to avoid generic array issues |
47 | | - List<List<T>> buckets = new ArrayList<>(bucketCount); |
48 | | - for (int i = 0; i < bucketCount; i++) { |
49 | | - buckets.add(new ArrayList<>()); |
50 | | - } |
| 35 | + private static class RobustComparator<T extends Comparable<T>> implements Comparator<T> { |
| 36 | + |
| 37 | + @Override |
| 38 | + @SuppressWarnings("unchecked") |
| 39 | + public int compare(T a, T b) { |
| 40 | + // Handle null values |
| 41 | + if (a == null && b == null) return 0; |
| 42 | + if (a == null) return -1; |
| 43 | + if (b == null) return 1; |
51 | 44 |
|
52 | | - // Distribute elements into buckets using double hashing |
53 | | - for (T element : array) { |
54 | | - int bucketIndex = getBucketIndex(element, bucketCount); |
55 | | - buckets.get(bucketIndex).add(element); |
56 | | - } |
| 45 | + // Handle floating point numbers with special values |
| 46 | + if (a instanceof Double && b instanceof Double) { |
| 47 | + return compareDoubles((Double) a, (Double) b); |
| 48 | + } |
| 49 | + if (a instanceof Float && b instanceof Float) { |
| 50 | + return compareFloats((Float) a, (Float) b); |
| 51 | + } |
57 | 52 |
|
58 | | - // Sort each bucket and collect results |
59 | | - int index = 0; |
60 | | - for (int i = 0; i < bucketCount; i++) { |
61 | | - List<T> bucket = buckets.get(i); |
62 | | - if (!bucket.isEmpty()) { |
63 | | - // Sort the bucket directly using Collections.sort |
64 | | - bucket.sort(null); |
65 | | - |
66 | | - // Copy sorted elements back to main array |
67 | | - for (T element : bucket) { |
68 | | - array[index++] = element; |
69 | | - } |
| 53 | + // Handle mixed number types |
| 54 | + if (a instanceof Number && b instanceof Number) { |
| 55 | + return compareNumbers((Number) a, (Number) b); |
70 | 56 | } |
| 57 | + |
| 58 | + // Handle strings (including empty strings) |
| 59 | + if (a instanceof String && b instanceof String) { |
| 60 | + return compareStrings((String) a, (String) b); |
| 61 | + } |
| 62 | + |
| 63 | + // Handle characters |
| 64 | + if (a instanceof Character && b instanceof Character) { |
| 65 | + return Character.compare((Character) a, (Character) b); |
| 66 | + } |
| 67 | + |
| 68 | + // Fallback to natural ordering |
| 69 | + return a.compareTo(b); |
71 | 70 | } |
72 | 71 |
|
73 | | - return array; |
74 | | - } |
| 72 | + private int compareDoubles(Double a, Double b) { |
| 73 | + // Handle NaN: NaN should come last |
| 74 | + if (Double.isNaN(a) && Double.isNaN(b)) return 0; |
| 75 | + if (Double.isNaN(a)) return 1; |
| 76 | + if (Double.isNaN(b)) return -1; |
75 | 77 |
|
76 | | - /** |
77 | | - * Calculates bucket index using double hashing technique |
78 | | - * |
79 | | - * @param element the element to hash |
80 | | - * @param bucketCount number of available buckets |
81 | | - * @return bucket index |
82 | | - */ |
83 | | - private <T extends Comparable<T>> int getBucketIndex(T element, int bucketCount) { |
84 | | - if (element == null) { |
85 | | - return 0; |
| 78 | + // Handle infinities |
| 79 | + if (a.equals(Double.NEGATIVE_INFINITY) && b.equals(Double.NEGATIVE_INFINITY)) return 0; |
| 80 | + if (a.equals(Double.NEGATIVE_INFINITY)) return -1; |
| 81 | + if (b.equals(Double.NEGATIVE_INFINITY)) return 1; |
| 82 | + |
| 83 | + if (a.equals(Double.POSITIVE_INFINITY) && b.equals(Double.POSITIVE_INFINITY)) return 0; |
| 84 | + if (a.equals(Double.POSITIVE_INFINITY)) return 1; |
| 85 | + if (b.equals(Double.POSITIVE_INFINITY)) return -1; |
| 86 | + |
| 87 | + // Normal comparison |
| 88 | + return Double.compare(a, b); |
| 89 | + } |
| 90 | + |
| 91 | + private int compareFloats(Float a, Float b) { |
| 92 | + // Handle NaN: NaN should come last |
| 93 | + if (Float.isNaN(a) && Float.isNaN(b)) return 0; |
| 94 | + if (Float.isNaN(a)) return 1; |
| 95 | + if (Float.isNaN(b)) return -1; |
| 96 | + |
| 97 | + // Handle infinities |
| 98 | + if (a.equals(Float.NEGATIVE_INFINITY) && b.equals(Float.NEGATIVE_INFINITY)) return 0; |
| 99 | + if (a.equals(Float.NEGATIVE_INFINITY)) return -1; |
| 100 | + if (b.equals(Float.NEGATIVE_INFINITY)) return 1; |
| 101 | + |
| 102 | + if (a.equals(Float.POSITIVE_INFINITY) && b.equals(Float.POSITIVE_INFINITY)) return 0; |
| 103 | + if (a.equals(Float.POSITIVE_INFINITY)) return 1; |
| 104 | + if (b.equals(Float.POSITIVE_INFINITY)) return -1; |
| 105 | + |
| 106 | + // Normal comparison |
| 107 | + return Float.compare(a, b); |
86 | 108 | } |
87 | 109 |
|
88 | | - // Primary hash function |
89 | | - int hash1 = Math.abs(element.hashCode()) % bucketCount; |
| 110 | + private int compareNumbers(Number a, Number b) { |
| 111 | + // Convert to double for comparison |
| 112 | + double da = a.doubleValue(); |
| 113 | + double db = b.doubleValue(); |
| 114 | + return Double.compare(da, db); |
| 115 | + } |
90 | 116 |
|
91 | | - // Secondary hash function (must be odd and different from bucket count) |
92 | | - int hash2 = 7 - (Math.abs(element.hashCode()) % 7); |
| 117 | + private int compareStrings(String a, String b) { |
| 118 | + // Handle empty strings properly - they should come before non-empty strings |
| 119 | + if (a.isEmpty() && b.isEmpty()) return 0; |
| 120 | + if (a.isEmpty()) return -1; |
| 121 | + if (b.isEmpty()) return 1; |
93 | 122 |
|
94 | | - // Double hashing formula: (hash1 + i * hash2) % bucketCount |
95 | | - // For simplicity, we use i = 1 here, but could be extended for collision resolution |
96 | | - return (hash1 + hash2) % bucketCount; |
| 123 | + // Normal string comparison |
| 124 | + return a.compareTo(b); |
| 125 | + } |
97 | 126 | } |
98 | 127 | } |
0 commit comments