主要排序算法测试笔记(java语言)

经试验, 桶排序 > 快速排序 > 归并排序/堆排序 > …

复杂度概览(copy过来的)
https://blog.csdn.net/zhc_24/article/details/82153471
https://blog.csdn.net/zhc_24/article/details/82153471

另, 桶排序 N + N ( l o g 2 N − l o g 2 M ) N+N(log^N_2 - log^M_2) N+N(log2Nlog2M) .

环境:

Idea编辑器

处理器 Intel® Core™ i7-8550U CPU @ 1.80GHz,2001 Mhz,4 个内核,8 个逻辑处理器

已安装的物理内存(RAM) 16.0 GB

工程: E:\Work\JAVA\DemoSet\hahacat\demo-mianshi

Yoga

处理器 Intel® Core™ i7-7500U CPU @ 2.70GHz,2901 Mhz,2 个内核,4 个逻辑处理器

已安装的物理内存(RAM) 8.00 GB

小结: 桶排序 > 快排 > 归并/堆排

归并排序

复杂度分析

[外链图片转存失败(img-BHCk1ME3-1568536386777)(assets/1568258590697.png)]

假设分解到每个叶子节点, 复杂度为常数 c , 树的高度为 l o g ( n + 1 ) log(n+1) log(n+1) , 则总复杂度: c ∗ n ∗ l o g ( n + 1 ) c*n*log(n+1) cnlog(n+1) , 即 O ( n l o g n ) O(nlog^n) O(nlogn) .

测试

数值范围: 1亿

1000W: 7460ms, 6781ms

规模耗时备注
10W195, 254, 253
100W636, 717, 686
1000W9967ms, 9830ms, Yoga: 9056ms, 9017ms, 消除了装箱拆箱: 6757ms
10000W太慢, 等不及结果出来

改为基本类型int后:

规模耗时备注
1000W2972ms, 2947ms, 2888ms
10000W25194ms

桶排序

复杂度分析

N个数据划分为M个桶, 求最大值最小值O(N), 每个桶内排序复杂度是 ( N / M ) l o g ( N / M ) (N/M)log(N/M) (N/M)log(N/M).

N + M ∗ ( N / M ) l o g 2 N / M = N + N ( l o g 2 N − l o g 2 M ) N+M*(N/M)log^{N/M}_2 = N+N(log^N_2 - log^M_2) N+M(N/M)log2N/M=N+N(log2Nlog2M)

最坏情况下, M=1, 会比一般的多出 O(N) 的损耗。但只要M达到2, 就会和一般的一样了, 随着M的增大, 复杂度越来越小. 当M=N时, 为 N .

补充说明三点(https://www.cnblogs.com/kkun/archive/2011/11/23/bucket_sort.html)

1,桶排序是稳定的

2,桶排序是常见排序里最快的一种,比快排还要快…大多数情况下

3,桶排序非常快,但是同时也非常耗空间,基本上是最耗空间的一种排序算法

桶数量问题

假如对数组nums进行桶排序,nums的长度为L,最小元素为A,最大元素为B。

则gap为(B-A)/L+1,桶的个数为(B-A)/gap+1。

另外一个重要的性质是,同一个桶中的元素相差最多为gap-1。

对nums中的元素nums[i],确定放入哪个桶的公式为:(nums[i]-A)/gap

https://blog.csdn.net/wusecaiyun/article/details/48048901

<java核心知识点.PDF> 中, 桶数量公式: ( m a x − m i n ) / l e n g t h + 1 (max-min)/length+1 (maxmin)/length+1 . 这是有缺陷的.

  1. 假设, [10, 100, 10000]. 得 3331 , 桶过多.
  2. [1, …, 10000]. 连续. 得 1 , 桶过少.

而换成, 先确定gap, 在确定桶数. 情形1, 桶数 3; 情形2, 桶数 10001 .

故, 暂推荐, 桶数算法:(并不好)
m x m i = m a x − m i n g a p = m x m i / l e n g t h + 1 b u c k e t N u m = m x m i / g a p + 1 mxmi = max-min \\ gap = mxmi / length +1 \\ bucketNum = mxmi / gap + 1 mxmi=maxmingap=mxmi/length+1bucketNum=mxmi/gap+1
测试(继续上面桶数算法的)

规模耗时备注
1000W18411ms, 18302ms, 桶内部排序改用java内置排序方法后: 13339ms

没有理论分析的那么快. 但当桶数量合适时, 明显快于归并.

1000W 不同桶数量时.

桶数耗时(ms)
17008
1005109
10004376
100001888
1000002204
10000002096
100000003790

可见要发挥桶排序的优势, 适当的桶数量很重要.

基数排序

基数排序用的就是桶排序思想.

对于整数, 从低位到高位, 趟. 按数字大小, 定位到对应的桶, 遍历完后, 将所有桶的数倒回源数组. 然后, 走下一个位(高一位). 位数由最大值决定. 不够的高位补0.

基数排序的另一个应用就是, 对字符串数组排序.

实验

对字符串数组, 用基于比较的排序方法和基数排序方法对比.

字符串最大长度: 100

规模基数内置归并
10W973242
100W11302, 11809, 10842995, 948, 876931, 871

注: 基数和归并用的是我自己写的.

字符串最大长度: 2

规模基数内置归并
100W19811421135

字符串最大长度设为10时, 三者差不多, 在800+ms.

堆排序

堆是一个完全二叉树, 但又有区别, 所谓’堆’, 顾名思义, 就是一堆, 金字塔一样, 堆尖是最小的, 而且每个小堆, 也是一样的堆尖是最小的. 满足堆序性质.

上滤 新增元素时, 依次和父节点比较, 小于父节点, 就要网上冒一层.

下滤 删除时涉及. 镂空的节点下沉.

  1. 建立最大堆. 假设初始乱序的数组就是一个不满足堆序的堆. 从最后一个非叶子节点开始, 自下而上, 将逐个小堆堆序化(下滤搞定). 遍历完成后, 堆就建立好了. 自上而下不行, 因为局部有序了, 在全局中并不是有序的. 而自下而上, 局部有序, 在更大范围中, 即使无序, 可以用下滤解决.
  2. 不停的删除堆顶, 删除后, 下滤, 保持堆序性. 然后将被删除的堆顶, 堆末尾空出来的位置.
  3. 删除完毕后, 数组自然就是升序了.

[外链图片转存失败(img-jdYUgKMc-1568536386778)(assets/1568454018519.png)]

实验

数值范围: 1亿

规模耗时备注
10W86, 105, 90略快于归并
100W1038, 848, 882略慢于归并
1000W16843, 14908
10000W太慢, 等不及结果出来

快速排序

  • 选取枢纽元(pivot)
  • 分割策略: 尽量保持两边平衡. 对于相等元素, 宁愿做无谓的交换.
  • 小数组时, 用插入排序或其他.
规模耗时备注
10W106, 96
100W423, 463
1000W4102, 4273, 4075
5000W29796, 29645, 28791

小数组改为插入排序后, 貌似好一点点.

规模耗时备注
10W
100W
1000W4218, 4225, 4187
5000W28629, 30305, 28260

插入排序

规模耗时备注
10W20860, 24290
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值