测试环境

  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. JS Leetcode 80. 删除有序数组中的重复项 II题解,常规解法与快慢双指针做法

    壹 ❀ 引 今天的题目来自LeetCode80. 删除有序数组中的重复项 II,是一道难度中等,但实际挺简单的一道题,题目描述如下: 给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每 ...

  2. Centos7和Centos8的NFS配置

    Centos7和Centos8的NFS配置几乎是完全一样的 服务端 Centos7默认安装了rpcbind, nfs-utils, 其中rpcbind的服务默认是启用的, nfs-utils默认是禁用 ...

  3. Set与WeakSet

    Set与WeakSet Set对象允许存储任何类型的唯一值,无论是原始值或者是对象引用,Set对象中的值不会重复. WeakSet对象允许存储对象弱引用的唯一值,WeakSet对象中的值同样不会重复, ...

  4. redis大key分析工具redis-rdb-tools

    最近1台云Redis的内存曝高,24G的内存占用19G,而且一直增长,想看那些key比较大,腾讯云Redis有大key分析的结果,但是这台没有,估计要找腾讯云的技术刷新一下数据: 分析大key工具,有 ...

  5. C++检测句柄的权限

    主要是依靠NtQueryObject函数,其中需要传入ObjectBasicInformation参数 PUBLIC_OBJECT_BASIC_INFORMATION结构包含可用于对象的全部信息的子集 ...

  6. 【Android逆向】破解看雪 test1.apk

    1. 获取apk,并安装至手机 apk 获取地址: https://www.kanxue.com/work-task_read-800624.htm adb install -t test1.apk ...

  7. Docker实践之08-使用网络

    目录 一.外部访问容器 启动容器时指定参数-P(大写P) 启动容器时指定参数-p(小写p) 二.容器互联 使用--link参数使容器互联 将容器加入自定义网络实现互联 三.为容器配置DNS 一.外部访 ...

  8. 2021-10-25 css中零值0后面是否要省略单位

    原理 在css中如果值为0,可以省略单位. 在css应用场景中,有可能是多端多人维护.即可能维护的人有A及B及C-,应用场景中有电脑端及手机端及小程序及打印机之类的. 结论 个人认为不要省略单位,不要 ...

  9. 【Azure 环境】使用 az ad group create 时候遇见 Insufficient privileges to complete the operation

    问题描述 使用China Azure,通过Azure CLI 创建AAD组报错,提示权限不足 Insufficient privileges to complete the operation # 使 ...

  10. 【Azure 应用服务】Storage Queue触发Azure Function时报错 The input is not a valid Base-64 string

    问题描述 创建一个PowerShell脚本的Azure Function,触发方式为 Storage Queue.但执行函数结果一直失败 (Failed). 错误消息为: Executed 'Func ...