8
8
* a positive value if `a` should come after `b`,
9
9
* and zero if `a` and `b` are considered equal.
10
10
*/
11
- type Comparator < T > = ( a : T , b : T ) => number
11
+ type Comparator < T > = ( a : T , b : T ) => number ;
12
12
13
13
// Minimum size of subarrays to be sorted using insertion sort before merging
14
- const MIN_MERGE = 32
14
+ const MIN_MERGE = 32 ;
15
+ const MIN_GALLOP = 7 ;
15
16
16
17
/**
17
18
* Merges two sorted subarrays into one sorted array with optimized galloping mode.
@@ -30,63 +31,57 @@ const merge = <T>(
30
31
rightIndex : number ,
31
32
compare : Comparator < T >
32
33
) : void => {
33
- const leftArrayLength = middleIndex - leftIndex + 1
34
- const rightArrayLength = rightIndex - middleIndex
34
+ const leftArrayLength = middleIndex - leftIndex + 1 ;
35
+ const rightArrayLength = rightIndex - middleIndex ;
35
36
36
37
// Create temporary arrays for the left and right subarrays
37
- const leftSubarray : T [ ] = arr . slice ( leftIndex , middleIndex + 1 )
38
- const rightSubarray : T [ ] = arr . slice ( middleIndex + 1 , rightIndex + 1 )
38
+ const leftSubarray : T [ ] = arr . slice ( leftIndex , middleIndex + 1 ) ;
39
+ const rightSubarray : T [ ] = arr . slice ( middleIndex + 1 , rightIndex + 1 ) ;
39
40
40
- let leftPointer = 0
41
- let rightPointer = 0
42
- let mergedIndex = leftIndex
41
+ let leftPointer = 0 ;
42
+ let rightPointer = 0 ;
43
+ let mergedIndex = leftIndex ;
43
44
44
- // Regular merge with galloping mode
45
+ // Merge the two subarrays back into the main array
45
46
while ( leftPointer < leftArrayLength && rightPointer < rightArrayLength ) {
46
- let numGallops = 0
47
-
48
- // Galloping through the left subarray
49
- while (
50
- leftPointer < leftArrayLength &&
51
- numGallops < MIN_MERGE &&
52
- compare ( leftSubarray [ leftPointer ] , rightSubarray [ rightPointer ] ) <= 0
53
- ) {
54
- arr [ mergedIndex ++ ] = leftSubarray [ leftPointer ++ ]
55
- numGallops ++
47
+ if ( compare ( leftSubarray [ leftPointer ] , rightSubarray [ rightPointer ] ) <= 0 ) {
48
+ arr [ mergedIndex ++ ] = leftSubarray [ leftPointer ++ ] ;
49
+ } else {
50
+ arr [ mergedIndex ++ ] = rightSubarray [ rightPointer ++ ] ;
56
51
}
57
52
58
- // Galloping through the right subarray
59
- while (
60
- rightPointer < rightArrayLength &&
61
- numGallops < MIN_MERGE &&
62
- compare ( rightSubarray [ rightPointer ] , leftSubarray [ leftPointer ] ) < 0
63
- ) {
64
- arr [ mergedIndex ++ ] = rightSubarray [ rightPointer ++ ]
65
- numGallops ++
66
- }
67
-
68
- // Standard merge without galloping
69
- while ( leftPointer < leftArrayLength && rightPointer < rightArrayLength ) {
70
- if (
71
- compare ( leftSubarray [ leftPointer ] , rightSubarray [ rightPointer ] ) <= 0
72
- ) {
73
- arr [ mergedIndex ++ ] = leftSubarray [ leftPointer ++ ]
53
+ // Implement galloping mode
54
+ let numGallops = 0 ;
55
+ while ( leftPointer < leftArrayLength && rightPointer < rightArrayLength && numGallops < MIN_GALLOP ) {
56
+ if ( compare ( leftSubarray [ leftPointer ] , rightSubarray [ rightPointer ] ) <= 0 ) {
57
+ arr [ mergedIndex ++ ] = leftSubarray [ leftPointer ++ ] ;
74
58
} else {
75
- arr [ mergedIndex ++ ] = rightSubarray [ rightPointer ++ ]
59
+ arr [ mergedIndex ++ ] = rightSubarray [ rightPointer ++ ] ;
76
60
}
61
+ numGallops ++ ;
62
+ }
63
+
64
+ // Gallop left
65
+ while ( leftPointer < leftArrayLength && compare ( leftSubarray [ leftPointer ] , rightSubarray [ rightPointer ] ) <= 0 ) {
66
+ arr [ mergedIndex ++ ] = leftSubarray [ leftPointer ++ ] ;
67
+ }
68
+
69
+ // Gallop right
70
+ while ( rightPointer < rightArrayLength && compare ( rightSubarray [ rightPointer ] , leftSubarray [ leftPointer ] ) < 0 ) {
71
+ arr [ mergedIndex ++ ] = rightSubarray [ rightPointer ++ ] ;
77
72
}
78
73
}
79
74
80
75
// Copy remaining elements from left subarray, if any
81
76
while ( leftPointer < leftArrayLength ) {
82
- arr [ mergedIndex ++ ] = leftSubarray [ leftPointer ++ ]
77
+ arr [ mergedIndex ++ ] = leftSubarray [ leftPointer ++ ] ;
83
78
}
84
79
85
80
// Copy remaining elements from right subarray, if any
86
81
while ( rightPointer < rightArrayLength ) {
87
- arr [ mergedIndex ++ ] = rightSubarray [ rightPointer ++ ]
82
+ arr [ mergedIndex ++ ] = rightSubarray [ rightPointer ++ ] ;
88
83
}
89
- }
84
+ } ;
90
85
91
86
/**
92
87
* Sorts an array using the Tim sort algorithm.
@@ -96,21 +91,7 @@ const merge = <T>(
96
91
* @param compare The comparator function defining the order of elements.
97
92
*/
98
93
export const timSort = < T > ( arr : T [ ] , compare : Comparator < T > ) : void => {
99
- const length = arr . length
100
-
101
- /**
102
- * Reverses a portion of the array.
103
- *
104
- * @param start The starting index of the portion to reverse.
105
- * @param end The ending index of the portion to reverse.
106
- */
107
- const reverseRange = ( start : number , end : number ) : void => {
108
- while ( start < end ) {
109
- const temp = arr [ start ]
110
- arr [ start ++ ] = arr [ end ]
111
- arr [ end -- ] = temp
112
- }
113
- }
94
+ const length = arr . length ;
114
95
115
96
/**
116
97
* Identifies runs and sorts them using insertion sort.
@@ -120,16 +101,31 @@ export const timSort = <T>(arr: T[], compare: Comparator<T>): void => {
120
101
*/
121
102
const findRunsAndSort = ( start : number , end : number ) : void => {
122
103
for ( let currIdx = start + 1 ; currIdx <= end ; currIdx ++ ) {
123
- const currentElement = arr [ currIdx ]
124
- let prevIdx = currIdx - 1
104
+ const currentElement = arr [ currIdx ] ;
105
+ let prevIdx = currIdx - 1 ;
125
106
126
107
while ( prevIdx >= start && compare ( arr [ prevIdx ] , currentElement ) > 0 ) {
127
- arr [ prevIdx + 1 ] = arr [ prevIdx ]
128
- prevIdx --
108
+ arr [ prevIdx + 1 ] = arr [ prevIdx ] ;
109
+ prevIdx -- ;
129
110
}
130
- arr [ prevIdx + 1 ] = currentElement
111
+ arr [ prevIdx + 1 ] = currentElement ;
131
112
}
132
- }
113
+ } ;
114
+
115
+ /**
116
+ * Calculates the minimum run length.
117
+ *
118
+ * @param n The length of the array.
119
+ * @returns The minimum run length.
120
+ */
121
+ const minRunLength = ( n : number ) : number => {
122
+ let r = 0 ;
123
+ while ( n >= MIN_MERGE ) {
124
+ r |= n & 1 ;
125
+ n >>= 1 ;
126
+ }
127
+ return n + r ;
128
+ } ;
133
129
134
130
/**
135
131
* Merges runs in the array.
@@ -139,50 +135,25 @@ export const timSort = <T>(arr: T[], compare: Comparator<T>): void => {
139
135
const mergeRuns = ( minRunLength : number ) : void => {
140
136
for ( let size = minRunLength ; size < length ; size *= 2 ) {
141
137
for ( let left = 0 ; left < length ; left += 2 * size ) {
142
- const mid = left + size - 1
143
- const right = Math . min ( left + 2 * size - 1 , length - 1 )
138
+ const mid = Math . min ( left + size - 1 , length - 1 ) ;
139
+ const right = Math . min ( left + 2 * size - 1 , length - 1 ) ;
144
140
145
141
if ( mid < right ) {
146
- merge ( arr , left , mid , right , compare )
142
+ merge ( arr , left , mid , right , compare ) ;
147
143
}
148
144
}
149
145
}
150
- }
151
-
152
- /**
153
- * Handles descending runs in the array.
154
- */
155
- const handleDescendingRuns = ( ) : void => {
156
- let stackSize = 0
157
- const runStack : [ number , number ] [ ] = [ ]
158
-
159
- // Push runs onto stack
160
- for ( let idx = 0 ; idx < length ; idx ++ ) {
161
- let runStart = idx
162
- while ( idx < length - 1 && compare ( arr [ idx ] , arr [ idx + 1 ] ) > 0 ) {
163
- idx ++
164
- }
165
- if ( runStart !== idx ) {
166
- runStack . push ( [ runStart , idx ] )
167
- }
168
- }
146
+ } ;
169
147
170
- // Merge descending runs
171
- while ( runStack . length > 1 ) {
172
- const [ start1 , end1 ] = runStack . pop ( ) !
173
- const [ start2 , end2 ] = runStack . pop ( ) !
174
-
175
- merge ( arr , start2 , end2 , end1 , compare )
176
- runStack . push ( [ start2 , end1 ] )
177
- }
178
- }
148
+ // Determine the minimum run length
149
+ const minRun = minRunLength ( length ) ;
179
150
180
151
// Find runs and sort them
181
- findRunsAndSort ( 0 , length - 1 )
152
+ for ( let i = 0 ; i < length ; i += minRun ) {
153
+ findRunsAndSort ( i , Math . min ( i + minRun - 1 , length - 1 ) ) ;
154
+ }
182
155
183
156
// Merge runs
184
- mergeRuns ( MIN_MERGE )
157
+ mergeRuns ( minRun ) ;
158
+ } ;
185
159
186
- // Handle descending runs
187
- handleDescendingRuns ( )
188
- }
0 commit comments