快速排序快速排序思想

1962年,由C.A.R.Hoare创造出来。该算法核心思想就一句话:“排序数组时,将数组分成两个小部分,然后对它们递归排序”。然而采取什么样的策略将数组分成两个部分是关键,想想看,如果随便将数组A分成A1和A2两个小部分,即便分别将A1和A2排好序,那么A1和A2重新组合成A时仍然是无序的。所以,我们可以在数组中找一个值,称为key值,我们在把数组A分解为A1和A2前先对A做一些处理,让小于key值的元素都移到其左边,所有大于key值的元素都移到其右边。这样递归排序A1和A2,数组A就排好了。

举例

我们要排序的数组如下:

55 41 59 26 53 58 97 93

我们选取第一个元素作为key值,即55.(一般都是选取第一个元素)。假如我们有一种办法可以将数组做一步预处理,让小于key值的元素都位于其左边,大于key值的元素都位于其右边,预处理完数组如下:

41 26 53 55 59 58 97 93

这样数组就被key值划分成了两段,A[0...2]小于key,A[4...7]大于key,可见key值本身已排好序,接下来对A[0...2]和A[4...7]分别进行递归排序,那么整个数组就排好序了。预处理做的工作再次澄清下:找一个key值,把key位放到某位置A[p],使小于key值的元素都位于A[p]左边,大于key值的元素都位于A[p]的右边。到此,我们的快排模型就成型了。

/*l, u 代表待排序部分的下界和上界*/

void qsort(l, u)

{

/*递归结束条件是待排序部分的元素个数小于2*/

if(l >= u)

{

return;

}

/*此处进行预处理,预处理后key值位于位置p*/

qsort(l, p-1);

qsort(p+1, u);

}

接下来看如何做预处理。我们选取A[0]做为key值, p作为key值的位置。我们从A[1]开始遍历后面的数组,用变量i指示目前的位置,每次找到小于key的值都与A[++p]交换。开始时p=0.

55 41 59 26 53 58 97 93 i = 1,A[i]位置为41, 即A[i] < key, swap(++p , i),即p = 1:

55 41 59 26 53 58 97 93 i = 2,A[i]位置为59,A[i] > key,不做任何改变。

i = 3,A[i]位置为26,A[i] < key,swap(++p, i), 即p = 2:

55 41 26 59 53 58 97 93 i = 4,A[i]位置为53,A[i] < key,swap(++p, i),p = 3:

55 41 26 53 59 58 97 93 i = 5,A[i]位置为58,A[i] > key,不做任何改变。

i = 6,A[i]位置为97,A[i] > key,不做任何改变.

i = 7,A[i]位置为93,A[i] > key,不做任何改变.结束循环。此时p为key的最终位置。还需一步把key值填入p位置。

最后swap(l, p)即把Key值放到最终位置上了。至于为什么要交换l,p的位置,可以另拿一组数据试一下:55,41,59,26,99,58,97,93。

完整的程序1

/*l, u 代表待排序部分的下界和上界*/

void qsort(int l, int u)

{

/*递归结束条件是待排序部分的元素个数小于2*/

if(l >= u)

{

return;

}

int p = l;

for(int i = l+1; i <= u; i++)

{

if(A[i] < A[l])

{

swap(++p, i);

}

}

swap(l, p);

qsort(l, p-1);

qsort(p+1, u);

}

这就是第一代快速排序算法,正常情况下其复杂度为nlogn,但在考虑一种极端情况:n个相同元素组成的数组。在n-1次划分中每次划分都需要O(n)的时间,所以总的时间为O(n^2)。使用双向划分就可以避免这个问题。

双向划分快速排序

/*l, u 代表待排序部分的下界和上界*/

void qsort(int l, int u)

{

/*递归结束条件是待排序部分的元素个数小于2*/

if(l >= u)

{

return;

}

key = A[l]

for(int i = l, j = u+1; i <= j;)

{

do i++ while(i <= u && A[i] < key));

do j-- while(A[j] > key);

if(i > j)

{

break;

}

swap(i, j);

}

swap(l, j);

qsort(l, j-1);

qsort(j+1, u);

}

插入排序优化插入排序的精髓就是首先将第一个元素视为有序子数组x[0...0],然后插入x[1]...x[n-1].思想很简单,代码也很简单,简单的代码有没有优化的空间呢?编程珠玑中提供了几个优化后的方案,效率提高了70%之多。

简单的实现(sort1)

void insertSort(int *array, size_t size)

{

for(size_t i = 1; i < size; i++)

{

for(int j = i; j > 0 && array[j - 1] > array[j]; j--)

{

swap(array[j - 1], array[j]);

}

}

}

优化思路

内循环的swap函数可能不如内联函数快些,所以第一步优化将该swap函数展开,据作者说,展开后效率提高了60%。

优化代码(sort2)

void insertSort(int *array, size_t size)

{

for(size_t i = 1; i < size; i++)

{

for(int j = i; j > 0 && array[j - 1] > array[j]; j--)

{

int t = array[j];

array[j] = array[j - 1];

array[j - 1] = t;

}

}

}

优化思路

由于内循环中总是给变量t赋同样的值(x[i]的初始值),所以内循环关于t的两条赋值语句移出循环,据说这么做的效率又提高了15%。

优化代码(sort3)

void insertSort(int *array, size_t size)

{

for(size_t i = 1; i < size; i++)

{

int j = i;

int t = array[j];

for(; j > 0 && array[j - 1] > array[j]; j--)

{

array[j] = array[j - 1];

}

array[j] = t;

}

}

《编程珠玑》书中给出了三种排序的运行时间:

插入排序的效率总是O(n2),效率差在比较的次数以及交换的频率,如果交换的频率减少的话就可以大大提高插入排序的效率,这也是为什么元素基本有序时插入排序效率高的原因。

个人观点

代码调优以及性能优化都可能带来一系列的副作用,比如程序的正确性,可读性,可维护性等。是否需要调优要看问题性质,调优既是华而不实的“花活”,也是一把利刃,区别就在于使用的场合。

c语言实现快速排序对文件中字符,C语言中快速排序和插入排序优化的实现相关推荐

  1. c语言程序的类型文件是什么,C语言中的文件结构类型FILE

    在c语言中的文件概述一文中我们说过在c语言中有两种类型的文件:ASCII文件和二进制文件.ASCII文件很简单就能搞定,新建一个txt文件,然后随便写点内容,保存就成了.但是二进制文件怎么写呢? 这个 ...

  2. c语言字符串赋初值并输出字符,C语言字符串使用指南

    写在前面:学习的第一门语言是Java,之前对C也了解一点,也只是了解一点,在加上长时间没有接触了,基本就只会一个Hello World了.现在由于准备升本考试,不得不从头开始学C.这里从零开始,记录C ...

  3. c语言输入数字误以为是字符,C语言初学者常见错误

    一.语言使用错误 在打代码的过程中,经常需要在中文与英文中进行转换,因此常出现一些符号一不小心就用错,用成中文.例如:":"中文中的分号占用了两个字节,而英文中";&qu ...

  4. C语言 define 防止头文件重复包含 - C语言零基础入门教程

    目录 一.头文件重复包含编译器报错 1.简单的理解头文件重复包 2.老流氓的理解头文件重复包 二.通过宏定义解决头文件重复包含 1.通过 #ifndef / #define 解决头文件重复包含 2.通 ...

  5. c语言将数据写入文件后乱码_C语言中写入文件的字符数组为乱码,但整形数据却正常!!!...

    在TC中~#include#include#include#include#includecharstr[10];staticcharstr2[10]="1234";structs ...

  6. c语言偏移一个字节,文件偏移量与C语言中的流定位

    一.文件偏移量 1.每个打开的文件都有一个与其相关联的"当前文件偏移量".它通常是一个非负整数,用以度量从文件开始处计算的字节数. 2.内核为所有打开的文件维持一张文件表.文件表项 ...

  7. c语言文件操作字符,C语言文件操作

    存储文件名:save.txt 程序代码如下: /* file display program. */ #include #define SIZE 100 main(int argc,char *arg ...

  8. C语言判断读取的文件内容字符编码是UTF-8还是GBK

    自定义两个字符编码判断函数 bool is_str_utf8(const char* str); bool is_str_gbk(const char* str); 测试文件 代码详细: #inclu ...

  9. c语言判断全角和半角字符,对于字符串中全角字符和半角字符的判断

    先拿空格来说: public class Test { private  String regex = "^[//u0020//u3000]*$"; public boolean ...

最新文章

  1. MySQL解压版安装
  2. 赠票 | 重磅揭晓Flink Forward Asia 2019完整议程!
  3. 总市值3862亿的创始人们在各阶段是怎么选女友的?
  4. Windows Phone 7 不温不火学习之《画图》
  5. Java类集框架 —— HashMap源码分析
  6. 如何分析 StackOverflow 异常 ?
  7. kfc流程管理炸薯条几秒_炸薯条成为数据科学的最后前沿
  8. Hadoop 基础系列一Hadoop 系列之 1.0 和2.0 架构
  9. mongo 修改器 $inc/$set/$unset/$pop/$push/$pull/$addToSet
  10. oracle 11g rac impdp,RAC创建DBlink并使用impdp抽取源库数据
  11. word在试图打开文件时遇到错误,检查稳定或驱动器文件权限
  12. 学有小成-php基础语法-06
  13. C语言--《C专家编程》C语言申明的优先级规则
  14. 服务器CPU与家用个人电脑CPU的区别详解
  15. 1. -vinc- = -vict- 胜利,征服
  16. 中国物流市场趋势报告、技术动态创新及市场预测
  17. 基于BP神经网络使用开盘价、最高价、最低价预测收盘价
  18. 嵌入式Linux的MiniGUI研究和移植
  19. [aria2c]使用aria2c下载“任务出错”的bt种子
  20. 如何将小鹤单字挂接到搜狗输入法

热门文章

  1. 20应用统计考研复试要点(part41)--概率论与数理统计
  2. 分类变量如何设置变量值的显示顺序
  3. java表格树_Java程序员值得拥有的TreeMap指南
  4. SAP UI5 应用开发教程之五十二 - 如何使用 SAP UI5 的标准控件结合 Cordova 插件调用手机摄像头进行条形码扫描试读版
  5. jMeter parallel controller 无法使用 CSV Data config 提供的变量?
  6. 本地修改远端 SAP UI5 框架文件的一个小技巧
  7. 如何使用 SAP Kyma 控制台手动发送 SAP Commerce Cloud Mock 应用暴露的事件
  8. rxjs of操作符传入数组的单步执行
  9. 程序员打造个人品牌 - Personal Brand的重要性
  10. Angular input控件的click事件响应处理的调用上下文