Timsort(泰姆排序)是一种混合排序算法,结合了合并排序(Merge Sort)和插入排序(Insertion Sort)的特性。它由Tim Peters在2002年为Python的排序算法而设计,并在Python 2.3版本中首次实现。
Tim Sort 是Python 的sorted()和list.sort()函数使用的默认排序算法。
自从该算法被发明以来,它已被用作Python、Java、Android平台和GNU Octave中的默认排序算法。Java中使用Java的标准库(java.util.Arrays类)来使用Timsort。
import java.util.Arrays;
public class TimsortExample {
public static void main(String\[\] args) {
// 示例数据
int\[\] array = {5, 2, 9, 1, 5, 6};
// 使用Arrays.sort()方法,底层使用Timsort
Arrays.sort(array);
// 打印排序后的结果
System.out.println("Sorted Array: " + Arrays.toString(array));
}
}
这个简单的示例演示了如何使用Java的Arrays.sort()方法对一个整数数组进行排序。在实际应用中,你可以用类似的方式对其他类型的数组或对象数组进行排序。
请注意,Java的Arrays.sort()方法使用了Timsort的实现,因此你不需要自己实现Timsort算法,除非你有特殊的需求。
Timsort:一种非常快速、O(n log n)、稳定的排序算法,专为现实世界构建,而不是在学术界构建。
Timsort的设计目的是克服其他排序算法在特定情况下的性能缺陷。它在实际应用中表现良好,尤其在处理有序数据或包含小规模子数组的数据时效果显著。
Timsort的主要思想是利用现实世界数据的一些特性,例如数据通常部分有序。它采用分而治之的策略,首先将数据分割成小的块,然后对这些块进行排序,最后再将这些有序块合并起来。该算法具有线性对数时间复杂度(O(n log n)),并且在最坏情况下的性能也比较好。
蒂姆排序算法
Tim Sort 背后的主要思想是利用数据中现有的顺序来最大限度地减少比较和交换的次数。它通过将数组划分为已排序的称为运行的小子数组,然后使用修改后的合并排序算法合并这些运行来实现此目的。
以下面的数组为例:arr\[\] = {4, 2, 8, 6, 1, 5, 9, 3, 7}。
- 步骤 1:定义运行的大小
最小运行规模:32(由于我们的数组较小,因此忽略这一步)
- 步骤 2:将阵列划分为运行
在这一步中,我们将使用插入排序对数组中的小子序列(运行)进行排序。
初始数组4, 2, 8, 6, 1, 5, 9, 3, 7
没有初始运行,因此我们将使用插入排序创建运行。
排序后的运行2, 4, 6, 8, 1, 5, 9, 3, 7
更新数组:2, 4, 6, 8, 1, 5, 9, 3, 7
- 步骤 3:合并运行
在这一步中,我们将使用修改后的合并排序算法合并已排序的运行。
合并运行,直到整个数组排序完毕。
合并运行2, 4, 6, 8, 1, 3, 5, 7, 9
更新后的数组:2, 4, 6, 8, 1, 3, 5, 7, 9
- 步骤 4:调整运行规模
每次合并操作后,我们都会将运行的大小加倍,直到超过数组的长度。
运行大小加倍:32、64、128(因为我们的数组很小,所以忽略这一步)
- 第 5 步:继续合并
重复合并过程,直到整个数组排序完毕。
最终合并的运行结果1, 2, 3, 4, 5, 6, 7, 8, 9
最终排序后的数组为 1,2,3,4,5,6,7,8,9。
下面是 TimSort 的实现:
// C++ program to perform TimSort.
include <bits/stdc++.h>
using namespace std;
const int RUN = 32;
// 该函数对数组进行排序,从左索引
// 索引排序到右索引,右索引是
// 最大的 RUN
void insertionSort(int arr\[\], int left, int right)
{
for (int i = left + 1; i <= right; i++) {
int temp = arri;
int j = i - 1;
while (j >= left && arrj > temp) {
arrj + 1 = arrj;
j--;
}
arrj + 1 = temp;
}
}
// 合并函数合并已排序的运行结果
void merge(int arr\[\], int l, int m, int r)
{
// 原始数组被分成两部分
// 左右两部分
int len1 = m - l + 1, len2 = r - m;
int leftlen1, rightlen2;
for (int i = 0; i < len1; i++)
lefti = arrl + i;
for (int i = 0; i < len2; i++)
righti = arrm + 1 + i;
int i = 0;
int j = 0;
int k = l;
// 比较之后,我们
// 合并这两个数组
// 合并为更大的子数组
while (i < len1 && j < len2) {
if (lefti <= rightj) {
arrk = lefti;
i++;
}
else {
arrk = rightj;
j++;
}
k++;
}
// 复制
// 如果有的话
while (i < len1) {
arrk = lefti;
k++;
i++;
}
// Copy remaining element of
// right, if any
while (j < len2) {
arrk = rightj;
k++;
j++;
}
}
//迭代 Timsort 函数,用于对
// 数组0...n-1排序(类似于合并排序)
void timSort(int arr\[\], int n)
{
// 对大小为 RUN 的单个子数组进行排序
for (int i = 0; i < n; i += RUN)
insertionSort(arr, i, min((i + RUN - 1), (n - 1)));
// 从 RUN(或 32)开始合并。
// 它将合并
// 形成大小为 64,然后是 128、256
// 以此类推 ....
for (int size = RUN; size < n; size = 2 * size) {
// 选择
// 左侧子数组的起点。我们
// 合并
// arrleft...left+size-1 和 arrleft+size, left+2\*size-1 合并。
// 和 arrleft+size, left+2\*size-1 进行合并。
// 每次合并后,我们
// 将 left 增加 2*size
for (int left = 0; left < n; left += 2 * size) {
//查找
// 左侧子数组
// mid+1 是右子数组的起点
//右子数组的起点
int mid = left + size - 1;
int right = min((left + 2 * size - 1), (n - 1));
//合并子数组 arrleft.....mid &
// arrmid+1....right
if (mid < right)
merge(arr, left, mid, right);
}
}
}
// Utility function to print the Array
void printArray(int arr\[\], int n)
{
for (int i = 0; i < n; i++)
printf("%d ", arri);
printf("\n");
}
// Driver program to test above function
int main()
{
int arr\[\] = { -2, 7, 15, -14, 0, 15, 0, 7,
-7, -4, -13, 5, 8, -14, 12 };
int n = sizeof(arr) / sizeof(arr0);
printf("Given Array is\n");
printArray(arr, n);
// Function Call
timSort(arr, n);
printf("After Sorting Array is\n");
printArray(arr, n);
return 0;
}
Java程序:
// Java program to perform TimSort.
class GFG {
static int MIN_MERGE = 32;
public static int minRunLength(int n)
{
assert n >= 0;
// Becomes 1 if any 1 bits are shifted off
int r = 0;
while (n >= MIN_MERGE) {
r |= (n & 1);
n >>= 1;
}
return n + r;
}
// This function sorts array from left index to
// to right index which is of size atmost RUN
public static void insertionSort(int\[\] arr, int left,
int right)
{
for (int i = left + 1; i <= right; i++) {
int temp = arri;
int j = i - 1;
while (j >= left && arrj > temp) {
arrj + 1 = arrj;
j--;
}
arrj + 1 = temp;
}
}
// Merge function merges the sorted runs
public static void merge(int\[\] arr, int l, int m, int r)
{
// Original array is broken in two parts
// left and right array
int len1 = m - l + 1, len2 = r - m;
int\[\] left = new intlen1;
int\[\] right = new intlen2;
for (int x = 0; x < len1; x++) {
leftx = arrl + x;
}
for (int x = 0; x < len2; x++) {
rightx = arrm + 1 + x;
}
int i = 0;
int j = 0;
int k = l;
// After comparing, we merge those two array
// in larger sub array
while (i < len1 && j < len2) {
if (lefti <= rightj) {
arrk = lefti;
i++;
}
else {
arrk = rightj;
j++;
}
k++;
}
// Copy remaining elements
// of left, if any
while (i < len1) {
arrk = lefti;
k++;
i++;
}
// Copy remaining element
// of right, if any
while (j < len2) {
arrk = rightj;
k++;
j++;
}
}
// Iterative Timsort function to sort the
// array0...n-1 (similar to merge sort)
public static void timSort(int\[\] arr, int n)
{
int minRun = minRunLength(MIN_MERGE);
// Sort individual subarrays of size RUN
for (int i = 0; i < n; i += minRun) {
insertionSort(
arr, i,
Math.min((i + MIN_MERGE - 1), (n - 1)));
}
// Start merging from size
// RUN (or 32). It will
// merge to form size 64,
// then 128, 256 and so on
// ....
for (int size = minRun; size < n; size = 2 * size) {
// Pick starting point
// of left sub array. We
// are going to merge
// arrleft..left+size-1
// and arrleft+size, left+2\*size-1
// After every merge, we
// increase left by 2*size
for (int left = 0; left < n; left += 2 * size) {
// Find ending point of left sub array
// mid+1 is starting point of right sub
// array
int mid = left + size - 1;
int right = Math.min((left + 2 * size - 1),
(n - 1));
// Merge sub array arrleft.....mid &
// arrmid+1....right
if (mid < right)
merge(arr, left, mid, right);
}
}
}
// Utility function to print the Array
public static void printArray(int\[\] arr, int n)
{
for (int i = 0; i < n; i++) {
System.out.print(arri + " ");
}
System.out.print("\n");
}
// Driver code
public static void main(String\[\] args)
{
int\[\] arr = { -2, 7, 15, -14, 0, 15, 0, 7,
-7, -4, -13, 5, 8, -14, 12 };
int n = arr.length;
System.out.println("Given Array is");
printArray(arr, n);
timSort(arr, n);
System.out.println("After Sorting Array is");
printArray(arr, n);
}
}
Python3
Python3 program to perform basic timSort
MIN_MERGE = 32
def calcMinRun(n):
"""Returns the minimum length of a
run from 23 - 64 so that
the len(array)/minrun is less than or
equal to a power of 2.
e.g. 1=>1, ..., 63=>63, 64=>32, 65=>33,
..., 127=>64, 128=>32, ...
"""
r = 0
while n >= MIN_MERGE:
r |= n & 1
n >>= 1
return n + r
This function sorts array from left index to
to right index which is of size atmost RUN
def insertionSort(arr, left, right):
for i in range(left + 1, right + 1):
j = i
while j > left and arrj < arrj - 1:
arrj, arrj - 1 = arrj - 1, arrj
j -= 1
Merge function merges the sorted runs
def merge(arr, l, m, r):
original array is broken in two parts
left and right array
len1, len2 = m - l + 1, r - m
left, right = \[\], \[\]
for i in range(0, len1):
left.append(arrl + i)
for i in range(0, len2):
right.append(arrm + 1 + i)
i, j, k = 0, 0, l
after comparing, we merge those two array
in larger sub array
while i < len1 and j < len2:
if lefti <= rightj:
arrk = lefti
i += 1
else:
arrk = rightj
j += 1
k += 1
Copy remaining elements of left, if any
while i < len1:
arrk = lefti
k += 1
i += 1
Copy remaining element of right, if any
while j < len2:
arrk = rightj
k += 1
j += 1
Iterative Timsort function to sort the
array0...n-1 (similar to merge sort)
def timSort(arr):
n = len(arr)
minRun = calcMinRun(n)
Sort individual subarrays of size RUN
for start in range(0, n, minRun):
end = min(start + minRun - 1, n - 1)
insertionSort(arr, start, end)
Start merging from size RUN (or 32). It will merge
to form size 64, then 128, 256 and so on ....
size = minRun
while size < n:
Pick starting point of left sub array. We
are going to merge arrleft..left+size-1
and arrleft+size, left+2\*size-1
After every merge, we increase left by 2*size
for left in range(0, n, 2 * size):
Find ending point of left sub array
mid+1 is starting point of right sub array
mid = min(n - 1, left + size - 1)
right = min((left + 2 * size - 1), (n - 1))
Merge sub array arrleft.....mid &
arrmid+1....right
if mid < right:
merge(arr, left, mid, right)
size = 2 * size
Driver program to test above function
if name == "main":
arr = [-2, 7, 15, -14, 0, 15, 0,
7, -7, -4, -13, 5, 8, -14, 12]
print("Given Array is")
print(arr)
Function Call
timSort(arr)
print("After Sorting Array is")
print(arr)
// C# program to perform TimSort.
using System;
class GFG {
public const int RUN = 32;
// This function sorts array from left index to
// to right index which is of size atmost RUN
public static void insertionSort(int\[\] arr, int left,
int right)
{
for (int i = left + 1; i <= right; i++) {
int temp = arri;
int j = i - 1;
while (j >= left && arrj > temp) {
arrj + 1 = arrj;
j--;
}
arrj + 1 = temp;
}
}
// merge function merges the sorted runs
public static void merge(int\[\] arr, int l, int m, int r)
{
// original array is broken in two parts
// left and right array
int len1 = m - l + 1, len2 = r - m;
int\[\] left = new intlen1;
int\[\] right = new intlen2;
for (int x = 0; x < len1; x++)
leftx = arrl + x;
for (int x = 0; x < len2; x++)
rightx = arrm + 1 + x;
int i = 0;
int j = 0;
int k = l;
// After comparing, we merge those two array
// in larger sub array
while (i < len1 && j < len2) {
if (lefti <= rightj) {
arrk = lefti;
i++;
}
else {
arrk = rightj;
j++;
}
k++;
}
// Copy remaining elements
// of left, if any
while (i < len1) {
arrk = lefti;
k++;
i++;
}
// Copy remaining element
// of right, if any
while (j < len2) {
arrk = rightj;
k++;
j++;
}
}
// Iterative Timsort function to sort the
// array0...n-1 (similar to merge sort)
public static void timSort(int\[\] arr, int n)
{
// Sort individual subarrays of size RUN
for (int i = 0; i < n; i += RUN)
insertionSort(arr, i,
Math.Min((i + RUN - 1), (n - 1)));
// Start merging from size RUN (or 32).
// It will merge
// to form size 64, then
// 128, 256 and so on ....
for (int size = RUN; size < n; size = 2 * size) {
// Pick starting point of
// left sub array. We
// are going to merge
// arrleft..left+size-1
// and arrleft+size, left+2\*size-1
// After every merge, we increase
// left by 2*size
for (int left = 0; left < n; left += 2 * size) {
// Find ending point of left sub array
// mid+1 is starting point of
// right sub array
int mid = left + size - 1;
int right = Math.Min((left + 2 * size - 1),
(n - 1));
// Merge sub array arrleft.....mid &
// arrmid+1....right
if (mid < right)
merge(arr, left, mid, right);
}
}
}
// Utility function to print the Array
public static void printArray(int\[\] arr, int n)
{
for (int i = 0; i < n; i++)
Console.Write(arri + " ");
Console.Write("\n");
}
// Driver program to test above function
public static void Main()
{
int\[\] arr = { -2, 7, 15, -14, 0, 15, 0, 7,
-7, -4, -13, 5, 8, -14, 12 };
int n = arr.Length;
Console.Write("Given Array is\n");
printArray(arr, n);
// Function Call
timSort(arr, n);
Console.Write("After Sorting Array is\n");
printArray(arr, n);
}
}
Javascript
<script> // Javascript program to perform TimSort. let MIN\_MERGE = 32; function minRunLength(n) { // Becomes 1 if any 1 bits are shifted off let r = 0; while (n >= MIN\_MERGE) { r |= (n & 1); n >>= 1; } return n + r; } // This function sorts array from left index to // to right index which is of size atmost RUN function insertionSort(arr,left,right) { for(let i = left + 1; i <= right; i++) { let temp = arr\i\\; let j = i - 1; while (j >= left && arr\j\\ > temp) { arr\j + 1\\ = arr\j\\; j--; } arr\j + 1\\ = temp; } } // Merge function merges the sorted runs function merge(arr, l, m, r) { // Original array is broken in two parts // left and right array let len1 = m - l + 1, len2 = r - m; let left = new Array(len1); let right = new Array(len2); for(let x = 0; x < len1; x++) { left\x\\ = arr\l + x\\; } for(let x = 0; x < len2; x++) { right\x\\ = arr\m + 1 + x\\; } let i = 0; let j = 0; let k = l; // After comparing, we merge those two // array in larger sub array while (i < len1 && j < len2) { if (left\i\\ <= right\j\\) { arr\k\\ = left\i\\; i++; } else { arr\k\\ = right\j\\; j++; } k++; } // Copy remaining elements // of left, if any while (i < len1) { arr\k\\ = left\i\\; k++; i++; } // Copy remaining element // of right, if any while (j < len2) { arr\k\\ = right\j\\; k++; j++; } } // Iterative Timsort function to sort the // array\0...n-1\\ (similar to merge sort) function timSort(arr, n) { let minRun = minRunLength(MIN\_MERGE); // Sort individual subarrays of size RUN for(let i = 0; i < n; i += minRun) { insertionSort(arr, i, Math.min( (i + MIN\_MERGE - 1), (n - 1))); } // Start merging from size // RUN (or 32). It will // merge to form size 64, // then 128, 256 and so on // .... for(let size = minRun; size < n; size = 2 \* size) { // Pick starting point // of left sub array. We // are going to merge // arr\left..left+size-1\\ // and arr\left+size, left+2\\\*size-1\\ // After every merge, we // increase left by 2\*size for(let left = 0; left < n; left += 2 \* size) { // Find ending point of left sub array // mid+1 is starting point of right sub // array let mid = left + size - 1; let right = Math.min((left + 2 \* size - 1), (n - 1)); // Merge sub array arr\left.....mid\\ & // arr\mid+1....right\\ if(mid < right) merge(arr, left, mid, right); } } } // Utility function to print the Array function printArray(arr,n) { for(let i = 0; i < n; i++) { document.write(arr\i\\ + " "); } document.write("
"); } // Driver code let arr = \ -2, 7, 15, -14, 0, 15, 0, 7, -7, -4, -13, 5, 8, -14, 12 \\; let n = arr.length; document.write("Given Array is
"); printArray(arr, n); timSort(arr, n); document.write("After Sorting Array is
"); printArray(arr, n); </script>
Output
Given Array is
-2 7 15 -14 0 15 0 7 -7 -4 -13 5 8 -14 12
After Sorting Array is
-14 -14 -13 -7 -4 -2 0 0 5 7 7 8 12 15 15