Tim排序(Tim Sort)

Tim排序(Tim Sort)

一、基本思想

Tim排序是一种混合稳定排序算法。它是由插入排序和归并排序衍生出来的混合算法。它首先使用插入排序得到子数组,这些小的排序子数组被称为自然运行(run)。然后使用归并排序堆这些运行进行合并子数组,产生最终的排序数组。它的设计考虑到算法在真实世界数据上的最佳性能。它利用了插入排序在小尺寸子数组上表现非常好的事实。它是Java的Array.sort()和Python的sorted()sort()使用的标准排序算法。

二、实现逻辑

假设有一个包含n元素的无序数组array,将考虑运行的大小为32。它可以是2的任何幂数,因为当数字是2的幂数时,合并更有效。

算法主体的步骤:

  1. 将数组化分为n/32运行;
  2. 使用插入排序函数对各个运行逐一进行排序;
  3. 使用归并排序的merge函数将排序后的运行逐一合并。

merge的步骤:

  1. 初始化辅助数组result来存储排序后的输出;
  2. 初始化3个指针ijki指向第一个子数组begin的开始,j指向第二个子数组mid+1的开始,kbegin初始化,保持排序数组result的当前索引;
  3. 直到到达array[begin,...,mid]array[mid+1,...,end]子数组的末尾,在索引i&j的元素中挑出一个较小的值,插入到result中;
  4. 当其中一个数组用完后,挑选剩余的元素插入到result中;
  5. result复制到array[begin,...,end]中。

三、时间复杂度的分析

若数组元素的个数小于run的话,那么直接进行插入排序,时间复杂度为: Ω ( n ) \Omega(n) Ω(n) Θ ( n 2 ) \Theta(n^2) Θ(n2) O ( n 2 ) O(n^2) O(n2),如果是二分插入排序的话,时间复杂度会减小。若数组元素的个数大于run的话,那么由于做run的插入排序会与归并排序产生并列的时间复杂度,故总时间复杂度为: Ω ( n log ⁡ 2 ( n ) ) \Omega(n \log_2(n)) Ω(nlog2(n)) Θ ( n log ⁡ 2 ( n ) ) \Theta(n \log_2(n)) Θ(nlog2(n)) O ( n log ⁡ 2 ( n ) ) O(n \log_2(n)) O(nlog2(n))

四、空间复杂度的分析

在下述两个算法中,并没有在主体开辟数组,但是在归并排序的过程中用到了额外的数组,故空间复杂度为: O ( n ) O(n) O(n)

五、算法实现

无二分优化

def tim_sort(array: list, run: int=32, reverse: bool=False) -> None:
    '''
    array: 支持数值型数据,如整型与浮点型混合;支持全为字符串类型的数据;不支持字符串型与数值型混合。
    run: 运行大小, 默认是32, 最好是2的幂数。
    reverse: 是否降序, 默认采用升序。
    '''
    def inser(l: int, r: int) -> None:
        '''
        l: 数据左侧游标(整型), r: 数据右侧游标(整型)
        '''
        for index in range(l + 1, r + 1):
            key = array[index]
            pre = index - 1
            while pre >= l and (key > array[pre] if reverse else key < array[pre]):
                array[pre + 1] = array[pre]
                pre -= 1
            array[pre + 1] = key
    
    def merge(low: int, mid: int, high: int) -> None:
        '''
        low: 数据低侧游标(整型), mid: 数据中间游标(整型), high: 数据高侧游标(整型)
        '''
        left = array[low: mid]
        right = array[mid: high]
        i = 0
        j = 0
        result = []
        while i < len(left) and j < len(right):
            if (left[i] > right[j] if reverse else left[i] <= right[j]):
                result.append(left[i])
                i += 1
            else:
                result.append(right[j])
                j += 1
        result += left[i:]
        result += right[j:]
        array[low: high] = result
    
    # choose run
    length = len(array)
    for index in range(0, length, run):
        inser(index, min(index + run - 1, length - 1))

    # merge run
    size = run
    while size < length:
        for low in range(0, length, 2 * size):
            mid = low + size
            high = min(low + 2 * size - 1, length - 1) + 1
            merge(low, mid, high)
        size = 2 * size

有二分优化

def tim_sort(array: list, run: int=32, reverse: bool=False) -> None:
    '''
    array: 支持数值型数据,如整型与浮点型混合;支持全为字符串类型的数据;不支持字符串型与数值型混合。
    run: 运行大小, 默认是32, 最好是2的幂数。
    reverse: 是否降序, 默认采用升序。
    '''
    def inser(l: int, r: int) -> None:
        '''
        l: 数据左侧游标(整型), r: 数据右侧游标(整型)
        '''
        for index in range(l + 1, r + 1):
            key = array[index]
            low, high = l, index - 1
            while low <= high: # 符合单调性的序列
                mid = (low + high) // 2
                if (key < array[mid] if reverse else key > array[mid]):
                    low = mid + 1
                else:
                    high = mid - 1
            for pre in range(index, low, -1): # 从后往前
                array[pre] = array[pre - 1]
            array[low] = key
    
    def merge(low: int, mid: int, high: int) -> None:
        '''
        low: 数据低侧游标(整型), mid: 数据中间游标(整型), high: 数据高侧游标(整型)
        '''
        left = array[low: mid]
        right = array[mid: high]
        i = 0
        j = 0
        result = []
        while i < len(left) and j < len(right):
            if (left[i] > right[j] if reverse else left[i] <= right[j]):
                result.append(left[i])
                i += 1
            else:
                result.append(right[j])
                j += 1
        result += left[i:]
        result += right[j:]
        array[low: high] = result
    
    # choose run
    length = len(array)
    for index in range(0, length, run):
        inser(index, min(index + run - 1, length - 1))

    # merge run
    size = run
    while size < length:
        for low in range(0, length, 2 * size):
            mid = low + size
            high = min(low + 2 * size - 1, length - 1) + 1
            merge(low, mid, high)
        size = 2 * size
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是Java实现的tim排序示例代码: ``` public class TimSort { private static final int MIN_MERGE = 32; private int[] arr; private int n; private int[] buffer; public void sort(int[] arr) { this.arr = arr; this.n = arr.length; this.buffer = new int[n]; int minRun = minRunLength(MIN_MERGE); for (int i = 0; i < n; i += minRun) { int end = Math.min(i + minRun, n); insertionSort(i, end); } for (int size = minRun; size < n; size <<= 1) { for (int left = 0; left < n; left += 2 * size) { int mid = left + size; int right = Math.min(left + 2 * size, n); merge(left, mid, right); } } } private void insertionSort(int start, int end) { for (int i = start + 1; i < end; i++) { int j = i; while (j > start && arr[j] < arr[j - 1]) { swap(j, j - 1); j--; } } } private void merge(int left, int mid, int right) { int i = left; int j = mid; int k = left; while (i < mid && j < right) { if (arr[i] < arr[j]) { buffer[k++] = arr[i++]; } else { buffer[k++] = arr[j++]; } } while (i < mid) { buffer[k++] = arr[i++]; } while (j < right) { buffer[k++] = arr[j++]; } for (k = left; k < right; k++) { arr[k] = buffer[k]; } } private void swap(int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } private int minRunLength(int minMerge) { int r = 0; while (n >= minMerge) { r |= (n & 1); n >>= 1; } return n + r; } } ``` 使用示例: ``` int[] arr = {5, 2, 4, 6, 1, 3}; TimSort timSort = new TimSort(); timSort.sort(arr); System.out.println(Arrays.toString(arr)); ``` 输出结果: ``` [1, 2, 3, 4, 5, 6] ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeGLMath

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值