Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5677141

Browse files
refactored TimSort algorithm
1 parent 9599d5d commit 5677141

File tree

2 files changed

+123
-123
lines changed

2 files changed

+123
-123
lines changed

‎src/main/kotlin/sorting/TimSort.kt‎

Lines changed: 98 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,126 @@
11
package sorting
22

3-
import java.lang.Integer.min
3+
import kotlin.math.min
44

55
/**
6-
* Tim sort algorithm
76
*
8-
* the best time: n
9-
* average time: n * log(n)
7+
* Tim Sort is a hybrid sorting algorithm derived from merge sort and insertion sort
8+
*
9+
* used by Python’s sorted() and list.sort() functions, which was designed to perform well on
10+
*
11+
* many kinds of real-world data.
12+
*
1013
* worst time: n * log(n)
14+
* best time: n
15+
* average time: n * log(n)
1116
*
1217
* amount of memory: n
1318
*
1419
*/
1520

16-
fun Array<Int>.timSort() {
17-
val size = size
18-
val minrun = minrun(size)
19-
var index = 0
20-
while (index < size) {
21-
insertionSort(this, index, min(index + minrun - 1, size - 1))
22-
index += minrun
23-
}
21+
class TimSort {
2422

25-
var mergingSize = minrun
26-
while (mergingSize < size) {
23+
fun sort(array: Array<Int>) {
24+
val arraySize = array.size
25+
val minRunSize = minRunSize(arraySize)
2726

28-
var left = 0
29-
while (left < size) {
30-
val middle = left + mergingSize - 1
31-
val right = min(left + 2 * mergingSize - 1, size - 1)
32-
if (middle < right) {
33-
merge(this, left, middle, right)
34-
}
35-
left += mergingSize * 2
27+
var i = 0
28+
while (i < arraySize) {
29+
insertionSort(array, i, min(i + minRunSize - 1, arraySize - 1))
30+
i += minRunSize
3631
}
3732

38-
mergingSize *= 2
39-
}
40-
}
41-
42-
private fun minrun(n: Int) : Int {
43-
var addedValue = 0
44-
var size = n
45-
if (size >= 64) {
46-
addedValue = addedValue or (size and 1)
47-
size = size.shr(1)
48-
}
49-
return size + addedValue
50-
}
51-
52-
private fun insertionSort(array: Array<Int>, left: Int, right: Int) {
53-
var outerIndex = left + 1
54-
while (outerIndex <= right) {
55-
val temporaryValue = array[outerIndex]
56-
var innerIndex = outerIndex - 1
57-
while (innerIndex >= left && array[innerIndex] > temporaryValue) {
58-
array[innerIndex + 1] = array[innerIndex]
59-
innerIndex--
33+
var mergingSize = minRunSize
34+
while (mergingSize < arraySize) {
35+
var start = 0
36+
while (start < arraySize) {
37+
val middle = start + mergingSize - 1
38+
val end = min(start + 2 * mergingSize - 1, arraySize - 1)
39+
if (middle < end) {
40+
merge(array, start, middle, end)
41+
}
42+
start += mergingSize * 2
43+
}
44+
mergingSize *= 2
6045
}
61-
array[innerIndex + 1] = temporaryValue
62-
outerIndex++
6346
}
64-
}
6547

66-
private fun merge(array: Array<Int>, left: Int, middle: Int, right: Int) {
67-
val leftLengthArray = middle - left + 1
68-
val rightLengthArray = right - middle
48+
/**
49+
* Minrun is chosen from the range 32 to 64 inclusive, such that the size of the data, divided by minrun, is equal to,
50+
* or slightly less than, a power of two. The final algorithm takes the six most significant bits of the size of the array,
51+
* adds one if any of the remaining bits are set, and uses that result as the minrun.
52+
* This algorithm works for all arrays, including those smaller than 64; for arrays of size 63 or less,
53+
* this sets minrun equal to the array size and Timsort reduces to an insertion sort
54+
*/
55+
private fun minRunSize(arraySize: Int) : Int {
56+
var result = 0
57+
var size = arraySize
58+
while (size >= 64) {
59+
result = result or (size and 1)
60+
// shift one bit to the right until 6 significant bits remain
61+
size = size shr 1
62+
}
6963

70-
var index = 0
71-
var leftArray = Array(leftLengthArray) { 0 }
72-
while (index < leftLengthArray) {
73-
leftArray[index] = array[left + index]
74-
index++
64+
return size + result
7565
}
7666

77-
index = 0
78-
var rightArray = Array(rightLengthArray) { 0 }
79-
while (index < rightLengthArray) {
80-
rightArray[index] = array[middle + 1 + index]
81-
index++
67+
private fun insertionSort(array: Array<Int>, start: Int, end: Int) {
68+
var i = start + 1
69+
while (i <= end) {
70+
val current = array[i]
71+
var j = i - 1
72+
while (j >= start && array[j] > current) {
73+
array[j + 1] = array[j]
74+
j--
75+
}
76+
array[j + 1] = current
77+
i++
78+
}
8279
}
8380

84-
var leftArrayIndex = 0
85-
var rightArrayIndex = 0
86-
index = 0
87-
88-
while (leftArrayIndex < leftLengthArray && rightArrayIndex < rightLengthArray) {
89-
if (leftArray[leftArrayIndex] <= rightArray[rightArrayIndex]) {
90-
array[index] = leftArray[leftArrayIndex]
91-
leftArrayIndex++
92-
} else {
93-
array[index] = rightArray[rightArrayIndex]
94-
rightArrayIndex++
81+
private fun merge(array: Array<Int>, start: Int, middle: Int, end: Int) {
82+
val leftSize = middle - start + 1
83+
val rightSize = end - middle
84+
85+
var i = 0
86+
val leftArray = Array(leftSize) { 0 }
87+
while (i < leftSize) {
88+
leftArray[i] = array[start + i]
89+
i++
9590
}
96-
index++
97-
}
9891

99-
while (leftArrayIndex < leftLengthArray) {
100-
array[index] = leftArray[leftArrayIndex]
101-
leftArrayIndex++
102-
index++
103-
}
92+
i = 0
93+
val rightArray = Array(rightSize) { 0 }
94+
while (i < rightSize) {
95+
rightArray[i] = array[middle + i + 1]
96+
i++
97+
}
98+
99+
i = 0
100+
var j = 0
101+
var k = start
102+
while (i < leftSize && j < rightSize) {
103+
if (leftArray[i] <= rightArray[j]) {
104+
array[k] = leftArray[i]
105+
i++
106+
} else {
107+
array[k] = rightArray[j]
108+
j++
109+
}
110+
k++
111+
}
112+
113+
while (i < leftSize) {
114+
array[k] = leftArray[i]
115+
i++
116+
k++
117+
}
104118

105-
while (rightArrayIndex < rightLengthArray) {
106-
array[index] = rightArray[rightArrayIndex]
107-
rightArrayIndex++
108-
index++
119+
while (j < rightSize) {
120+
array[k] = rightArray[j]
121+
j++
122+
k++
123+
}
109124
}
125+
110126
}
Lines changed: 25 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,35 @@
11
package sorting
22

33
import org.junit.Test
4-
import org.junit.Assert.assertEquals
4+
import kotlin.random.Random
5+
import org.junit.Assert.assertArrayEquals
56

67
class TimSortTest {
78

89
@Test
9-
fun test_reversed_array() {
10-
val expected = TestUtils.list(100000)
11-
12-
val actual = expected.reversed().toTypedArray()
13-
actual.timSort()
14-
15-
assertEquals(expected, actual.toList())
16-
}
17-
18-
@Test
19-
fun test_random_array() {
20-
val actual = TestUtils.randomArray(50000)
21-
22-
val expected = actual.sorted()
23-
24-
actual.timSort()
25-
26-
assertEquals(expected, actual.toList())
27-
}
28-
29-
@Test
30-
fun test_shuffled_array() {
31-
val expected = TestUtils.sortedArray(100000)
32-
33-
val actual = expected.copyOf()
34-
actual.shuffle()
35-
actual.timSort()
36-
37-
assertEquals(expected.toList(), actual.toList())
38-
}
39-
40-
@Test
41-
fun test_sorted_array() {
42-
val actual = TestUtils.sortedArray(100000)
43-
44-
val expected = actual.toList()
45-
46-
actual.timSort()
47-
48-
assertEquals(expected, actual.toList())
10+
fun `test sort`() {
11+
val timSort = TimSort()
12+
13+
val expected1 = Array(1_000_000) { it }
14+
val actual1 = expected1.reversedArray()
15+
timSort.sort(actual1)
16+
assertArrayEquals(expected1, actual1)
17+
18+
val actual2 = Array(1_000_000) { Random.nextInt(1_000_000) }
19+
val expected2 = actual2.sortedArray()
20+
timSort.sort(actual2)
21+
assertArrayEquals(expected2, actual2)
22+
23+
val expected3 = Array(1_000_000) { it }
24+
val actual3 = expected3.copyOf()
25+
actual3.shuffle()
26+
timSort.sort(actual3)
27+
assertArrayEquals(expected3, actual3)
28+
29+
val expected4 = Array(1_000_000) { it }
30+
val actual4 = expected3.copyOf()
31+
timSort.sort(actual3)
32+
assertArrayEquals(expected4, actual4)
4933
}
5034

5135
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /