插入排序、快速排序,你还记得吗?

本文深入探讨了插入排序和快速排序的多种实现方式,并对比了它们在不同情况下的性能表现。

算法往往是我们入门软件开发的一个基础,而排序,一般是算法中的基础。我一直觉得,自己的基础没有打好,自己学计算机也是半路出家,到现在还后悔上大学的时候没有疯狂的学习。闲话不说了,说说今天的主题。

我试着让我写的东西可以浅显易懂,毕竟,我一直觉得,突然间看别人写的代码总是很别扭的

问:你会写插入排序吗?

答: 我们至少曾经会的,现在很多人都已经忘记了,以下是我的代码

用的是List<int>而没有选有数组,这个不是重点,重点是,总觉得写的很乱,不信你看看下面的插入排序,是书上用数组实现的

 

我相信,如果你像我一样笨写出了第一种代码,那么看到第二种实现方式后会有一种冲动,我靠,太牛了。尤其是把最里面花括号中代码封装为那个著名的swap(int a,int b)之后。

但是,其实,光做到这一步,还远远不够,请看下面的插入排序

这段代码省去了swap()方法,因为我们从前往后遍历依次使用swap后达到的效果其实就是把后面的一个数字移到前面的某个地方然后把中间的数字全部向后退一格。我们来看看效率吧,我获取了小于200,000的190,000个不重复的随机的数数组,下图中iSort1是第二种方法,iSort3是第三种方法,(第一种list<int>方式我就不测试了)前者是接近三分三十秒,后者只有125毫秒,这个,不是差的一点两点吧

而iSort2,我是突然手痒,试用swap的另外一种实现方式arrInt[j - 1] = arrInt[j - 1] - arrInt[j];arrInt[j] = arrInt[j - 1] + arrInt[j];arrInt[j - 1] = arrInt[j] - arrInt[j - 1];终于发现原来这种非主流的方法比常规的方法慢。

不过话说回来,iSort的效率这么高,真的是意想不到。

 

问:你是怎么调用这几个方法并且方便快捷的处理和显示运行时间的?

答: 我还是和上一篇一样,用了策略模式,然后让这些排序的方法继承一个抽象类

且看代码

非常简单的抽象类

下面只是对类的一个调用,大家也可以直接跳过

 

然后在main方法中写如下代码

List<ChooseSort2> listCS2 = new List<ChooseSort2>();//以泛型的方式存储以下几种类
listCS2.Add(new ChooseSort2(new iSort1(), count));
listCS2.Add(new ChooseSort2(new iSort2(), count));
listCS2.Add(new ChooseSort2(new iSort3(), count));

int Num = 1;
Console.WriteLine("以下为循环" + Num + "次一共所需的时间");
foreach (ChooseSort2 cs2 in listCS2)
{
    cs2.ToSort(arrInt, Num);
}

就ok了

 

 

问:你还记得大明湖畔的快速排序吗?

答: 只借助大脑的情况下,我真的记得不是很清楚了,不过我最后还是写了出来

后来我发现,写出这种代码,只能用来考计算机二级,和之后的代码一比,速度,实在是太慢、太慢了,根本就不是什么快速排序我突然觉得,自己还有还有很多很多东西要去学习,甚至是最简单的排序算法

 

书中的思路是这样的,数组分成四段,第一段,第一个数字(下标为low,大小假设为t),第二段,存放小于等于t的数字,第三段,大于等于t的数字,最后一段是未知,我们集中在第三段进行排序,排序完成后将第一段的t移到中间去

紧接着,有出现了另外一种方法

数组分成四段,第一段,第一个数字(下标为low,大小假设为t),第二段,存放小于等于t的数字,最后一段是大于等于t的数字,中间一段是未知,我们集中在第三段进行排序,排序完成后将第一段的t移到中间去,简而言之,作者称为双向划分,可以避免最坏情况的出现(未排序时就已经是顺序的)

我们来看一下效果吧

因为我生成的数列是19000位的随机数列,所以这里没有测试极端情况,故而qSort2和qSort3效果差不多,但是,我的qSort1就再一次差的不是一点半点了。

最后,作者又提出了另一个思路:在quickSort方法的中的第一步判断if(l>u)return改动以下,if(u-l)<cutoff return,也就是说,作者认为快速排序花费了大量的精力对小范围进行排序,所以决定对这小范围改用其他排序,改动方法倒是很简单,

1.cutoff赋值

2.依照上面改动if语句

3.调用iSort进行排序

但是我发现,效果不是很明显,可能我们的编程环境和当时作者的环境有所区别吧。

关于cutoff的取值,我是从1开始每次乘以2的方式进行试探, for (int i = 1; i < 200000; i += i),感觉效果还真有点(和qSort3比较)

就先写到这儿吧,欢迎大家指正,有什么写的不清楚的地方也欢迎提意见,需要完整代码可以留下邮箱

 

 

问:快速排序还有啥补充的

答:这一部分本来是要写在评论里的,但是不清楚优快云总是不允许我评论,所以我只好以编辑原文的方式补上了

快速排序我后来又试了之前的代码实现,(之前是list<int>,我改成了数组),发现运行时间只有上述方法中qSrot3的40%左右,不过在极端情况下应该没有qSort3快吧,顺便贴上代码

 

 

 

仔细比较了一下,其实快速排序的这几种都是大同小异,至于“小异”怎么讲呢,我且细说:
1.参照物的不同
    qSort1,qSort2,qSort3,都是取的第一个数字作为比较的参照物,这样的有一个缺点,一直要用第一个数字,为了方便起见这三种方法都选择了将其始终保持在第一位,直至一轮qSort方法完成后再通过一个swap()调过去,这一步调用,就显得拖沓。
2.跳出quickSort的时机不同,qSort0的代码如下
if (left < j) 
    quickSort(left, j); 
if (right > i) 
    quickSort(i, right);
而qSort1,qSort2,qSort3则是在调用quickSort后进行判断
所以说,真的是大同小易
我觉得,通过这样一番比较,对软件开发中的基本用法有了更深的了解,或者是,本人还是处于打基础阶段
3.排序变换数值顺序的方法不同
qSort1是除了最后一个数字集体向后退一,然后把最后一个数字放到最前面,这无疑不是什么好方法,当然,要比一步步冒泡好一点
而qSort2,qSort3,qSort0都是找准位置,一个swap搞定
4.貌似都有点冗余

<think>嗯,用户想比较希尔排序和快速排序的差异及应用场景。首先,我需要回顾这两种算法的基本概念和工作原理。 希尔排序是基于插入排序的改进版本,通过分组和逐步缩小间隔来减少元素的移动次数。记得引用中提到,希尔排序在某些情况下效率很高,甚至比快速排序更快,但平均来看快速排序更优[^1][^3]。而快速排序则是分治算法,通过选取基准元素分区处理,递归排序。 接下来是区别。希尔排序是不稳定的,快速排序同样不稳定,但可能用户需要明确这一点。时间复杂度方面,希尔排序的平均复杂度是$O(n \log n)$到$O(n^2)$之间,具体取决于间隔序列;而快速排序平均是$O(n \log n)$,最坏情况下$O(n^2)$,不过可以通过优化避免最坏情况。空间复杂度,希尔排序是原地排序,$O(1)$;快速排序需要递归栈,平均$O(\log n)$,最坏$O(n)$。 性能对比部分,引用指出快速排序在实际中通常最快,但希尔排序可能在数据部分有序或小规模时表现更好[^1][^2]。适用场景的话,希尔排序适合中小规模数据或内存受限环境,快速排序适合大规模随机数据,尤其是需要平均高效的情况。 还要考虑应用场景的具体例子,比如嵌入式系统可能用希尔排序,而通用库用快速排序。同时,用户可能需要知道优化方法,比如快速排序的三数取中或切换到插入排序,这点引用里提到希尔排序的适用场景[^4]。 最后生成相关问题时要覆盖区别、性能分析、选择依据、优化方法等,帮助用户深入理解。需要确保引用正确,比如引用[1]和[3]提到希尔排序的效率,引用[2]比较了不同算法的适用场景,引用[4]解释了希尔排序的分组策略。</think>### 希尔排序与快速排序的对比分析 #### 一、核心区别 1. **算法思想** - **希尔排序**:基于插入排序的改进,通过分组(间隔序列$gap$)逐步缩小范围,减少元素的移动距离。 例如,初始$gap=n/2$,每组进行插入排序,最终$gap=1$时完成全局排序。 - **快速排序**:采用分治策略,选择基准元素(如第一个元素$pivot$),将数组划分为“小于$pivot$”和“大于等于$pivot$”的子数组,递归排序。 2. **时间复杂度** - **希尔排序**:取决于$gap$序列,平均约为$O(n \log n) \sim O(n^{1.5})$,最坏$O(n^2)$[^3]。 - **快速排序**:平均$O(n \log n)$,最坏$O(n^2)$(如数组已有序且基准选择不当),但通过随机化基准或三数取中可优化至$O(n \log n)$。 3. **空间复杂度** - **希尔排序**:原地排序,$O(1)$。 - **快速排序**:递归栈空间平均$O(\log n)$,最坏$O(n)$。 4. **稳定性** 两者均**不稳定**,例如希尔排序的分组交换可能破坏相等元素顺序,快速排序的分区交换同理。 --- #### 二、性能对比 | 指标 | 希尔排序 | 快速排序 | |---------------|----------------------------|----------------------------| | **平均时间** | $O(n^{1.5})$ | $O(n \log n)$ | | **最坏时间** | $O(n^2)$ | $O(n^2)$(可优化避免) | | **内存占用** | 极低(原地排序) | 中等(递归栈空间) | | **适用数据** | 中小规模、部分有序数据 | 大规模、随机分布数据 | | **实际效率** | 特定场景可能优于快排 | 通常为实际最快排序算法 | --- #### 三、适用场景 1. **希尔排序**: - **嵌入式系统**:内存受限且数据规模较小时。 - **部分有序数据**:插入排序的高效性在$gap=1$时发挥作用[^3]。 - **简单实现需求**:代码实现较快速排序更简洁。 2. **快速排序**: - **通用库函数**(如C++ `std::sort`):随机数据下效率稳定。 - **大规模数据集**:通过并行化或尾递归优化进一步提升性能。 - **需要平均高效性**:避免最坏情况后,综合性能优于堆排序和归并排序。 --- #### 四、典型代码示例 **希尔排序(Python)** ```python def shell_sort(arr): n = len(arr) gap = n // 2 while gap > 0: for i in range(gap, n): temp = arr[i] j = i while j >= gap and arr[j - gap] > temp: arr[j] = arr[j - gap] j -= gap arr[j] = temp gap //= 2 return arr ``` **快速排序(Python)** ```python def quick_sort(arr): if len(arr) <= 1: return arr pivot = arr[len(arr) // 2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quick_sort(left) + middle + quick_sort(right) ``` --- #### 五、总结 - **选择希尔排序**:当数据规模较小、内存敏感或数据部分有序时。 - **选择快速排序**:处理大规模随机数据,且需要平均高效性时[^2]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值