福哥答案2021-01-14:
答案来自此链接:
介绍:
timsort是一种混合、稳定高效的排序算法,源自合并排序和插入排序,旨在很好地处理多种真实数据。它由Tim Peters于2002年实施使用在Python编程语言中。该算法查找已经排序的数据的子序列,并使用该知识更有效地对其余部分进行排序。这是通过将已识别的子序列(称为运行)与现有运行合并直到满足某些条件来完成的。从版本2.3开始,Timsort一直是Python的标准排序算法。如今,Timsort 已是是 Python、 Java、 Android平台 和 GNU Octave 的默认排序算法。

思想:
针对现实中需要排序的数据分析看,大多数据通常是有部分已经排好序的数据块,Timsort 就利用了这一特点。Timsort 称这些已经排好序的数据块为 “run”,我们可以将其视为一个一个的“分区”。在排序时,Timsort迭代数据元素,将其放到不同的 run 里,同时针对这些 run ,按规则进行合并至只剩一个,则这个仅剩的 run 即为排好序的结果。
换句话说,就是分析待排序数据,根据其本身的特点,将排序好的(不管是顺序还是逆序)子序列的分为一个个run分区,当然,这个分区run也存在一定的约束,即根据序列会产生一个minrun,如果原始的run小于minrun的长度,用插入排序扩充run,直到达到条件,之后使用归并排序来合并多个run。

知乎:
首先,timsort是Python里默认的排序算法,直接就可以在cPython的源码里找到,我没记错的话好像是600多行。
timsort改进自归并排序,因为待排序数据中是一定存在一些连续递增和连续严格递减子序列的,那么timsort会找到这样的子序列,称其为run。之后便是把严格递减的run反向,整个序列就变成了好多好多个递增的run。
然后就是使用归并排序的方式merge相邻的run,等到数组中只剩下一个run的时候自然就排好序了。
实际实现时,扫描出一个run就要分析一下已有的runs要不要合并,主要是通过最后面的两到三个run的长度来进行判断。
如果初始run的数量恰好为2的整数次幂或者略小于2的整数次幂,可以进一步避免长度差距太大的两个run的合并。(如果一个run的长度大于另一个run的两倍,就可以认为差距过大了)
所以要对长度过短的run使用插入排序进行扩充,最终要保证初始run的长度在32和64之间(记不清边界条件了,没敢写成区间形式),这样可以保证长度过短时用插入排序提高效率,初始run的长度较为接近,数量也保证了后续不会存在过多的差距过大的run的合并。
在合并的时候也没有使用普通的归并排序的方式,但唯独这一小块我还不太了解。之前自己用C++语言写过一个不完整的timsort,自认为还算是比较了解的,当然合并不同的run我用的是普通的归并排序的方式。

时间有限,timesort只是了解了大概。代码参考了其他文献,用go语言改写。代码里是非原地排序。代码如下:

package main

import (
"fmt"
"math/rand"
"time"
) //https://blog.csdn.net/sinat_35678407/article/details/82974174
func main() {
rand.Seed(time.Now().Unix())
SucCount := 0
FaiedCount := 0
for i := 0; i < 1000; i++ {
arr1 := NewRandArr()
arr2 := make([]int, len(arr1))
copy(arr2, arr1)
fmt.Println("原数组:", arr1)
arr1 = timsort(arr1)
fmt.Println("timsort排序:", arr1)
SelectionSort(arr2)
fmt.Println("选择排序:", arr2) isEqual := true
for j := 0; j < len(arr1); j++ {
if arr1[j] != arr2[j] {
isEqual = false
fmt.Println("错误")
break
}
}
if isEqual {
SucCount++
} else {
FaiedCount++
}
fmt.Println("----")
}
fmt.Println("成功 = ", SucCount)
fmt.Println("失败 = ", FaiedCount) }
func binary_search(arr []int, left int, right int, value int) int {
if left >= right {
if arr[left] <= value {
return left + 1
} else {
return left
}
} else {
mid := left + (right-left)>>1
if arr[mid] < value {
return binary_search(arr, mid+1, right, value)
} else {
return binary_search(arr, left, mid-1, value)
}
}
}
func insertion_sort(arr []int) []int {
arrLen := len(arr)
ret := make([]int, 0)
for i := 1; i < arrLen; i++ {
value := arr[i]
pos := binary_search(arr, 0, i-1, value) ret = append(ret, arr[:pos]...)
ret = append(ret, value)
ret = append(ret, arr[pos:i]...)
ret = append(ret, arr[i+1:]...)
}
return ret
}
func merge(l1 []int, l2 []int) []int {
l1Len := len(l1)
if l1Len <= 0 {
return l2
}
l2Len := len(l2)
if l2Len <= 0 {
return l1
}
ret := make([]int, 0)
if l1[0] < l2[0] {
ret = append(ret, l1[0])
ret = append(ret, merge(l1[1:], l2)...)
} else {
ret = append(ret, l2[0])
ret = append(ret, merge(l1, l2[1:])...)
}
return ret
}
func timsort(arr []int) []int {
arrLen := len(arr)
if arrLen <= 1 {
return arr
}
runs := make([][]int, 0)
//sorted_runs := make([][]int, 0)
new_run := []int{arr[0]}
for i := 1; i < arrLen; i++ {
if arr[i] < arr[i-1] {
runs = append(runs, new_run)
new_run = []int{arr[i]}
} else {
new_run = append(new_run, arr[i])
}
if arrLen-1 == i {
runs = append(runs, new_run)
break
}
}
for i := 0; i < len(runs); i++ {
insertion_sort(runs[i])
}
sorted_arr := make([]int, 0)
for i := 0; i < len(runs); i++ {
sorted_arr = merge(sorted_arr, runs[i])
}
//fmt.Println(sorted_arr)
return sorted_arr } //选择排序
func SelectionSort(arr []int) {
arrlen := len(arr)
if arrlen < 2 {
return
}
// 0~n-1
// 1~n-1
// 2~n-1
for i := 0; i < arrlen; i++ { // i ~ N-1
// 最小值在哪个位置上 i~n-1
minIndex := i
for j := i + 1; j < arrlen; j++ { // i ~ N-1 上找最小值的下标
if arr[j] < arr[minIndex] {
minIndex = j
}
}
arr[i], arr[minIndex] = arr[minIndex], arr[i]
}
} //产生一个随机数组
func NewRandArr() []int { Len := rand.Intn(100) + 1
ret := make([]int, Len)
for i := 0; i < Len; i++ {
ret[i] = rand.Intn(1000)
}
return ret
}

执行结果如下:


2021-01-14:timsort是什么,如何用代码实现?
Timsort——自适应、稳定、高效排序算法
2021-01-14:timsort是什么,如何用代码实现?
评论

2021-01-14:timsort是什么,如何用代码实现?的更多相关文章

  1. app后端设计(2)--xmpp的使用(2014.01.14更新)

    在app中有时候是需要添加聊天服务,在这里谈谈曾经开发聊天服务的经验: (1)聊天服务端选的openfire,这是一个基于xmpp协议的聊天服务器(XMPP是一种基于XML的协议,它继承了在XML环境 ...

  2. 2021.11.14 CF1583E Moment of Bloom(LCA+图上构造)

    2021.11.14 CF1583E Moment of Bloom(LCA+图上构造) https://www.luogu.com.cn/problem/CF1583E 题意: She does h ...

  3. 2021.05.14 tarjan

    2021.05.14 tarjan 标准版tarjan 这里使用数组来模拟栈 void tarjan(int x){ ++ind; dfn[x]=low[x]=ind; stacki[++top]=x ...

  4. MOSS2010中如何用代码给托管元数据类型的栏目赋值

    最近项目中遇到如何用代码给托管元数据类型的栏目赋值问题,经过折腾,现把我的思路和实现方法共享出来,让大家一起来学习学习.相互探讨下. /// <summary> /// 托管元数据 /// ...

  5. 如何用代码的方式取出SAP C4C销售订单创建后所有业务伙伴的数据

    比如我创建了一个Sales Order(销售订单)后,如何用代码的方式取出这些通过SAP Partner determination自动填充的Involved Parties信息呢? 一种方法可以使用 ...

  6. Android | 教你如何用代码一键实现银行卡绑定

    前言   小编前面几期文章分别给大家介绍了用代码实现微笑抓拍.证件照DIY.拍照翻译的功能开发(链接见文章末尾),本次小编给大家带来的是用代码一键实现银行卡识别与绑定功能. 银行卡识别的应用场景    ...

  7. 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 02 封装的代码实现

    088 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 02 封装的代码实现 本文知识点:Java封装的代码实现 说明:因为时间紧张,本人写博客过程中只 ...

  8. Noip模拟76 2021.10.14

    T1 洛希极限 上来一道大数据结构或者单调队列优化$dp$ 真就没分析出来正解复杂度 正解复杂度$O(q+nm)$,但是据说我的复杂度是假的 考虑一个点转移最优情况是从它上面的一个反$L$形转移过来 ...

  9. Noip模拟53 2021.9.14

    T1 ZYB和售货机 首先这道题有两种做法. 一种是发现每个点都可以先被取到只剩一个,只要收益大于$0$ 然后发现建一个$i->f[i]$的图时出现环,要把它去掉, 那么跑一个$tarjan$枚 ...

  10. Noip模拟39 2021.8.14

    T1 打地鼠 都切掉了的简单题 1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 con ...

随机推荐

  1. Git——Git 常用命令

    文章目录 仓库 配置 增加/删除文件 代码提交 分支 标签 查看信息 远程同步 撤销 其他 仓库 # 在当前目录新建一个Git代码库 $ git init # 新建一个目录,将其初始化为Git代码库 ...

  2. MyBatis foreach循环批量修改数据时报错

    报错如下 org.springframework.jdbc.BadSqlGrammarException: ### Error updating database. Cause: java.sql.S ...

  3. 《最新出炉》系列初窥篇-Python+Playwright自动化测试-22-处理select下拉框-上篇

    1.简介 在实际自动化测试过程中,我们也避免不了会遇到下拉框选择的测试,因此宏哥在这里直接分享和介绍一下,希望小伙伴或者童鞋们在以后工作中遇到可以有所帮助.今天,我们讲下playwright的下拉框怎 ...

  4. MATLAB(Octave)命令记录

    1. struct:结构体数组 aaaa_s.a = 1; aaaa_s.b = {'A','B','C'} aaaa_s.c = [1 2 3 4; 5 6 7 8]; save aaaa_s.tx ...

  5. Go 接口:Go中最强大的魔法,接口应用模式或惯例介绍

    Go 接口:Go中最强大的魔法,接口应用模式或惯例介绍 目录 Go 接口:Go中最强大的魔法,接口应用模式或惯例介绍 一.前置原则 二.一切皆组合 2.1 一切皆组合 2.2 垂直组合 2.2.1 第 ...

  6. Aspire 框架预览版发布,使云原生开发和运维更加简单

    随着 .NET 8 的发布,.NET Aspire 也随之发布,这是一个全家桶框架旨在加快基于云的应用程序的构建..NET Aspire 从一开始就集成了关键组件,例如遥测和运行状况检查.它还承诺提供 ...

  7. CTA策略介绍

    CTA策略更多的时候是一种投资方法,更准确的说,主要投资于衍生品的.比较系统化规则化的投资方法都可以称作CTA投资,它并不拘泥于量化或是主动,其具有相当的生命力,会长期存在. CTA策略的收入来源是多 ...

  8. 学习JavaScript的第一天

    JavaScript概述 JavaScript的介绍 js属于一门面向对象的编程语言 属于跨平台 面向对象(oop) 以对象方式实现所有的功能 跨平台:js代码不论是在什么样的操作系统上执行结果都是一 ...

  9. [AGC034D] Manhattan Max Matching

    Problem Statement Snuke is playing with red and blue balls, placing them on a two-dimensional plane. ...

  10. 开源地图库OpenLayers的简单使用

    引言 最近在学习可视化的东西,这让我想起了一些以前用过的图表库,其实我在日常做的大多是普通的需求,可视化方面应用的并不多,只是偶尔会因为个别特殊的需求,去借助一些图表库来实现图表的展示,这些普通的图表 ...