测试环境

  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. Linux中echo的作用

    Linux echo命令 echo命令是一个内置在Bash中的shell,通常用于shell脚本中以显示消息或输出其他命令的结果. echo命令的语法如下: 1 echo [-neE] [ARGUME ...

  2. Transform LiveData

    查询资料的其中一个场景: 创建一个回调函数,当查询后台的时候,后台有结果了,回调对应的回调函数,并将结果保存到LiveData中. public class DataModel {     ...   ...

  3. sensitive-word-admin 敏感词控台 v1.2.0 版本开源

    开源目的 丰富 sensitive-word 的生态. 提供最基本的操作例子,便于在此基础上丰富实现自己的敏感词控台. 避免重复开发的成本,让更多的精力专注于业务. 拓展阅读 sensitive-wo ...

  4. Springboot AOP介绍及实战

    介绍 AOP是Aspect Oriented Program的首字母缩写:这种在运行时,动态地将代码切入到类的指定方法.指定位置上的编程思想就是面向切面的编程. 主要用于非核心业务处理,比如权限,日志 ...

  5. ElementUI导入Excel文件

    功能介绍 最近用ElementUI做管理系统需要把excel数据导入到系统内,我想这是一个很常见的功能点,把它分享出来,希望对大家有所帮助:) 实现效果 实现步骤 1.定义导入组件 <el-up ...

  6. 李宏毅2022机器学习HW3 Image Classification

    Homework3 数据集下载 在本地环境下进行实验总是令人安心,但是又苦于网上找不到数据集,虽然kaggle上有数据集但是下载存在问题 于是有了一个天才的想法,间接从kaggle上下载(利用outp ...

  7. 系统环境变量中 HTTPS_PROXY 的误区

    前段时间在测试一个连麦 demo,demo 简要说可以在内网环境中运行时,输入频道号就可以模拟连麦 但是在加入连麦时,一直返回错误 -2 EOF,询问得知,该错误的解释信息是"Service ...

  8. Hexo - 搭建个人博客的bug集合

    按照很多视频教程进行操作,发现到hexo d这一步后,无法部署到github远端. 目前的解决方法: npm un hexo-deployer-git npm i hexojs/hexo-deploy ...

  9. etcd实现分布式锁分析

    3篇关于分布式锁的文章,可以结合看: consul实现分布式锁:https://www.cnblogs.com/jiujuan/p/10527786.html redis实现分布式锁:https:// ...

  10. 命令行解析parse.parse_know_args()

    简介 在接受到多余的命令行参数时不报错,只把第一个参数作为当前使用的命令参数, 剩余部分留给其它程序使用,返回一个tuple类型的命名空间和一个保存着余下的命令行字符的list 示例 import a ...