快速排序遇到的小bug
测试环境
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的更多相关文章
- Chrome出了个小bug:论如何在Chrome下劫持原生只读对象
Chrome出了个小bug:论如何在Chrome下劫持原生只读对象 概述 众所周知,虽然JavaScript是个很灵活的语言,浏览器里很多原生的方法都可以随意覆盖或者重写,比如alert.但是为了保证 ...
- 解决JqueryUI 拖放排序遇到滚动条时有可能无法执行排序的小bug
前些日子不是在做 使用Jquery-UI实现一次拖拽多个选中的元素操作嘛,在持续完善这个组件时遇到了一个关于拖放排序的bug.今天就着图片和代码重现一下,也顺便告诉大家如何解决这个问题. 首先先上图描 ...
- 淘宝WAP版小BUG分析
前几天发现的一个淘宝WAP版的小BUG,就是用桌面版chrome看的时候产品评价中的图片显示不出来,都是图裂了. 这是什么原因呢?图片为什么会显示不出来呢?淘宝的技术人员.测试人员不可能没发现啊.开启 ...
- 关于一个小bug的修正
python初学者,非常喜欢虫师的文章. 练习时发现一个小bug,http://www.cnblogs.com/fnng/p/3782515.html 验证邮箱格式一题中,第三个x不允许有数字,但是测 ...
- 用 parseInt()解决的 小 bug
在做轮播模块的时候遇到问题是:你在 连续指示小按钮 时候再去 只有 点击 下一张按钮,出现bug: 指示小按钮的 className 当前显示的 calssName 为 undefined ! // ...
- iOS开发之使用UICollectionView实现美团App的分类功能【偶现大众点评App的一个小bug】
郝萌主倾心贡献,尊重作者的劳动成果,请勿转载. 假设文章对您有所帮助,欢迎给作者捐赠,支持郝萌主,捐赠数额任意,重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源代码下载:点我传送 游戏官方下 ...
- Flex Validator的小BUG
Flex中对同一控件如TextInput进行多种格式校验的情况下,如不注意,可能导致错误信息不显示的BUG,比如 <fx:Array id="validators"> ...
- Flutter实战视频-移动电商-34.列表页_小BUG的修复
34.列表页_小BUG的修复 当高粱酒的子类没有数据返回的时候就会报错. 解决接口空数据报错的问题 没有数据的时候,给用户一个友好的提示, 我们没有数据的时候还要告诉用户,提示一下他没有数据,在我们的 ...
- 观CSDN站点小Bug有感
今天早上在浏览博客的时候偶然发现CSDN博客的数据出现了异常,我也是头一次看到这么明显的Bug.详细什么表现呢?先来看个截图.例如以下: 常常看CSDN博客的人 ...
- Fundebug前端JavaScript插件更新至1.8.2,修复2个小BUG
摘要: 修复2个BUG,请大家及时更新. Fundebug前端异常监控服务 Fundebug是专业的程序异常监控平台,我们JavaScript插件可以提供全方位的异常监控,可以帮助开发者第一时间定位各 ...
随机推荐
- NC24949 [USACO 2008 Jan S]Running
题目链接 题目 题目描述 The cows are trying to become better athletes, so Bessie is running on a track for exac ...
- MySQL 幻象行
当同一个查询在不同的时间产生不同的行集时,就会出现所谓的幻像问题.例如,如果执行了两次SELECT,但是第二次返回了第一次没有返回的行,那么该行就是一个"幻象"行. 假设在表chi ...
- CentOS7 开机网卡加载失败
服务器CentOS7一开,发现web服务无法访问.最终用ifconfig发现,网卡没有加载,连个IP地址都没有. 这时使用命令 service network restart 试图重启服务器网络.不料 ...
- Oracle ascii函数
一 简介 Oracle ascii函数用于返回单个字符的数字代号. 二 语法 ASCII( single_character ) 参数说明: 代表只能输入单个字符,如果输入多个,oracle只会返 ...
- Oracle驱动错误:oracle.jdbc.driver.T4CConnection.isValid(I)Z
1.问题说明 今天在sping boot中配置多数据源,用到了oracle和postgresql两种数据库. oracle驱动版本是ojdbc14,启动以后调试程序直接报错了,就是本文题目中的错. 查 ...
- CentOS 8安装RabbitMQ
第一步:安装yum仓库 导入签名KEY: ## primary RabbitMQ signing key ## 这一步如果因为网络问题下载不成功,可以先将签名文件下载下来,本地导入 rpm --imp ...
- 多线程系列(九) -ReentrantLock常用方法详解
一.简介 在上一篇文章中,我们介绍了ReentrantLock类的一些基本用法,今天我们重点来介绍一下ReentrantLock其它的常用方法,以便对ReentrantLock类的使用有更深入的理解. ...
- linux下安装nginx(编译安装)及反向代理及负载均衡
首先卸载掉之前用yum命令下载的nginx yum remove nginx 安装nginx需要的依赖库 yum install -y gcc patch libffi-devel python-de ...
- 【ACM专项练习#02】整行字符串、输入vector、打印图形、处理n组数据以及链表操作等
输入整行字符串 平均绩点 题目描述 每门课的成绩分为A.B.C.D.F五个等级,为了计算平均绩点,规定A.B.C.D.F分别代表4分.3分.2分.1分.0分. 输入 有多组测试样例.每组输入数据占一行 ...
- 基于Python GDAL为长时间序列遥感图像绘制时相变化曲线图
本文介绍基于Python中gdal模块,对大量多时相栅格图像,批量绘制像元时间序列折线图的方法. 首先,明确一下本文需要实现的需求:现有三个文件夹,其中第一个文件夹存放了某一研究区域原始的多时 ...