测试环境

  Ubuntu 18.04, gcc 8.4

复习一下快排算法,不料却得到了非预期的结果。示例代码如下

 1 #include <stdio.h>
2
3 void mySwap(int *p, int *q)
4 {
5 *p ^= *q;
6 *q ^= *p;
7 *p ^= *q;
8 }
9
10 void getPivot(int *srcArr, int left, int right)
11 {
12 int mid = left + (right-left)/2;
13
14 if(srcArr[left] > srcArr[mid])
15 {
16 mySwap(&srcArr[left], &srcArr[mid]);
17 }
18
19 if(srcArr[mid] > srcArr[right])
20 {
21 mySwap(&srcArr[mid], &srcArr[right]);
22 }
23
24 if(srcArr[left] > srcArr[right])
25 {
26 mySwap(&srcArr[mid], &srcArr[right]);
27 }
28 }
29
30 void quickSort(int *srcArr, int left, int right)
31 {
32 if (left >= right)
33 {
34 return;
35 }
36
37 int low = left;
38 int high = right;
39
40 int pivot;
41 //getPivot(srcArr, low, high);
42
43 int mid = low + (high-low)/2;
44
45 mySwap(&srcArr[low], &srcArr[mid]);
46
47 pivot = srcArr[low];
48
49 while (low < high)
50 {
51 while (srcArr[high] >= pivot && low < high)
52 {
53 --high;
54 }
55
56 srcArr[low] = srcArr[high];
57
58 while (srcArr[low] <= pivot && low < high)
59 {
60 ++low;
61 }
62
63 srcArr[high] = srcArr[low];
64
65 }
66
67 srcArr[low] = pivot;
68 quickSort(srcArr, left, low-1);
69 quickSort(srcArr, low+1, right);
70 }
71
72 int main()
73 {
74 int srcArr[10] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
75
76 quickSort(srcArr, 0, 9);
77
78 for(int i = 0; i < 10; ++i)
79 {
80 printf("%d ", srcArr[i]);
81 }
82
83 putchar(10);
84
85 return 0;
86 }

运行结果如下

第一个元素为0,本应该是1的。在这个过程中我并没有修改数组中的元素呀,为什么出现了0呢?为什么只有第一个元素有问题呢?其它的元素为什么没有问题呢?

使用gdb调试,在排序的序列尽量靠近左侧时打印下标和元素的值,发现了一个非预期的现象。当排序第0个和第1个元素这两个元素所在的序列时,执行到截图中第44行(交换两个元素的值)代码后,下标都没有问题,low = 0, mid = 0, high = 1。

问题是第0个元素值此时变成了0了。只是一个交换,第0个元素咋就变成0了呢?再看看交换两个元素的值是通过异或来进行的,一下子反应过来了,估计是与自身异或了。任何整数与自身异或结果为0。

(截图中的行号与前面示例代码中的行号有点出入,请知悉)

调整,在交换之前判断下是否为同一个元素,为不同的元素才进行交换。

1 if (low != mid)
2 {
3 mySwap(&srcArr[low], &srcArr[mid]);
4 }

再次运行

结果符合预期。

再回到这个问题,为什么只有第一个元素有问题呢?其它的元素为什么没有问题呢?

按照上面提供的数组中的数据,复盘了下排序的过程,排序过程中,中轴两边的序列(左边的元素小于中轴,右边的元素大于中轴),前面的几轮中,右边都只有一个元素。最后几轮中,刚好有一轮是4个元素,即第0个到第3个元素。排序后,中轴左边剩下两个元素,即经0个和第1个元素,中轴右侧只剩下第3个元素。

这两个元素的序列在排序前,先进行了交换,想着尽可能取中间的元素作为中轴(假设中间的元素为均值),于是将最左侧的元素与序列中间的元素进行了交换。注意,此时,中间的元素与第0个元素的下标一致,即中间的元素即为第0个元素。相同的元素异或,结果为0,结果使第0个元素值为0了

这也导致了问题的出现。

从复盘过程中也明显感受到了快速排序的弊端,中轴的值取得不好的情况,像示例代码中,总是导致中轴一侧仅有一个元素,导致了最坏的情况和最坏的时间复杂度。取合适的中轴的值是值得思考的,取得一个好的中间的值,可以使得中轴两的序列分布比较均匀,可以凸显快排的优势。一是可以采用随机算法,得到一个随机的下标,但这也不能保证它一定就是最优的,另外随机算法也需要一定的开销。二是,取首元素、中间元素、尾元素三者中的中间者作为中间元素(即比较三者大小取中间者),这也要求序列中至少有三个元素,当然这个也是在拼人品。并不能保证中轴值最为合理。

下面示例代码采用了第二种方式实现了一下,为保证序列中至少有三个元素,增加了判断条件。

 1         int low = left;
2 int high = right;
3 int center = (left+right)/2;
4 int pivot;
5
6 if(right - left >= 2)
7 {
8 if(arr[left] > arr[right])
9 {
10 mySwap(&arr[left], &arr[right]);
11 }
12
13 if(arr[center] > arr[right])
14 {
15 mySwap(&arr[center], &arr[right]);
16 }
17
18 if(arr[left] > arr[center])
19 {
20 mySwap(&arr[left], &arr[center]);
21 }
22
23 mySwap(&arr[left], &arr[center]);
24 }
25
26 pivot = arr[left];

加个题外话,如果元素数量较少(如少于20)时,可以选择其它排序算法如插入排序等。上面的示例代码仅是作复习快速排序算法用的。

注:因为bug是后面重新复现的,截图的调试中代码行号与示例代码中的行号有3行内的误差,请知悉。

参考材料:

《数据结构与算法分析 C语言实现》 马克.艾伦.维斯

快速排序遇到的小bug的更多相关文章

  1. Chrome出了个小bug:论如何在Chrome下劫持原生只读对象

    Chrome出了个小bug:论如何在Chrome下劫持原生只读对象 概述 众所周知,虽然JavaScript是个很灵活的语言,浏览器里很多原生的方法都可以随意覆盖或者重写,比如alert.但是为了保证 ...

  2. 解决JqueryUI 拖放排序遇到滚动条时有可能无法执行排序的小bug

    前些日子不是在做 使用Jquery-UI实现一次拖拽多个选中的元素操作嘛,在持续完善这个组件时遇到了一个关于拖放排序的bug.今天就着图片和代码重现一下,也顺便告诉大家如何解决这个问题. 首先先上图描 ...

  3. 淘宝WAP版小BUG分析

    前几天发现的一个淘宝WAP版的小BUG,就是用桌面版chrome看的时候产品评价中的图片显示不出来,都是图裂了. 这是什么原因呢?图片为什么会显示不出来呢?淘宝的技术人员.测试人员不可能没发现啊.开启 ...

  4. 关于一个小bug的修正

    python初学者,非常喜欢虫师的文章. 练习时发现一个小bug,http://www.cnblogs.com/fnng/p/3782515.html 验证邮箱格式一题中,第三个x不允许有数字,但是测 ...

  5. 用 parseInt()解决的 小 bug

    在做轮播模块的时候遇到问题是:你在 连续指示小按钮 时候再去 只有 点击 下一张按钮,出现bug: 指示小按钮的 className 当前显示的 calssName 为 undefined ! // ...

  6. iOS开发之使用UICollectionView实现美团App的分类功能【偶现大众点评App的一个小bug】

    郝萌主倾心贡献,尊重作者的劳动成果,请勿转载. 假设文章对您有所帮助,欢迎给作者捐赠,支持郝萌主,捐赠数额任意,重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源代码下载:点我传送 游戏官方下 ...

  7. Flex Validator的小BUG

    Flex中对同一控件如TextInput进行多种格式校验的情况下,如不注意,可能导致错误信息不显示的BUG,比如 <fx:Array id="validators"> ...

  8. Flutter实战视频-移动电商-34.列表页_小BUG的修复

    34.列表页_小BUG的修复 当高粱酒的子类没有数据返回的时候就会报错. 解决接口空数据报错的问题 没有数据的时候,给用户一个友好的提示, 我们没有数据的时候还要告诉用户,提示一下他没有数据,在我们的 ...

  9. 观CSDN站点小Bug有感

            今天早上在浏览博客的时候偶然发现CSDN博客的数据出现了异常,我也是头一次看到这么明显的Bug.详细什么表现呢?先来看个截图.例如以下:             常常看CSDN博客的人 ...

  10. Fundebug前端JavaScript插件更新至1.8.2,修复2个小BUG

    摘要: 修复2个BUG,请大家及时更新. Fundebug前端异常监控服务 Fundebug是专业的程序异常监控平台,我们JavaScript插件可以提供全方位的异常监控,可以帮助开发者第一时间定位各 ...

随机推荐

  1. Pandas resample数据重采样

    随机抽样,是统计学中常用的一种方法,它可以帮助我们从大量的数据中快速地构建出一组数据分析模型.在 Pandas 中,如果想要对数据集进行随机抽样,需要使用 sample() 函数. sample() ...

  2. CF739A Alyona and mex 题解

    题目传送门 前置知识 贪心 | 构造 解法 从贪心的角度分析,最小的 \(\operatorname{mex}\) 仅会与长度最小的区间有关:另外为使得 \(\operatorname{mex}\) ...

  3. burpsuit+adb+逍遥模拟器

    安卓7之后,单纯的将burpsuit的证书导出手动安装到模拟器中已经不行了,app可以只信任指定证书和系统内置的证书,后续用户安装的证书是不生效的,只能想办法装到系统内部 需要将证书通过openssl ...

  4. Python中`yield`关键字详解

    Python中`yield`关键字有什么用? Python中yield关键字有什么用? 它能做什么? 例如,我试图理解这段代码1: def _get_child_candidates(self, di ...

  5. EmuELEC 4.3 安装和乐视手柄 LeWGP-201 evremap问题解决

    一年多前安装了EmuELEC3.9之后, 就一直没有再更新过, 平时玩玩小游戏也很正常. 昨天心血来潮想把吃灰的乐视手柄用起来, 结果发现3.9里面没有evremap 命令. 猜测可能是这个版本的问题 ...

  6. C++ 多线程的错误和如何避免(8)

    不要重复获取同一个锁 问题:在获得一个锁并且没有释放该锁的前提下,再次尝试获取该锁会报错. 比如, #include <iostream> #include <thread> ...

  7. Programming Abstractions in C阅读笔记:p293-p302

    <Programming Abstractions in C>学习第73天,p293-p302总结,总计10页. 一.技术总结 1.时间复杂度 (1)quadratic time(二次时间 ...

  8. 记一次 splice 导致 io.Copy 阻塞的排查过程

    记一次 splice 导致 io.Copy 阻塞的排查过程 简而言之,net.TCPConn 的 ReadFrom 零拷贝实现 splice 在 1.21.0 - 1.21.4 删除了 SPLICE_ ...

  9. 在矩池云使用Llama2-7B的方法

    今天给大家分享如何在矩池云服务器使用 Llama2-7b模型. 硬件要求 矩池云已经配置好了 Llama 2 Web UI 环境,显存需要大于 8G,可以选择 A4000.P100.3090 以及更高 ...

  10. 【Azure Redis 缓存】应用中出现连接Redis服务错误(production.ERROR: Connection refused)的排查步骤

    问题描述 在PHP应用中,连接Redis的方法报错  RedisException(code: 0): Connection refused at /data/Redis/Connectors/Php ...