`
chenzehe
  • 浏览: 532118 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

快速排序算法分析及程序示例

阅读更多
实例说明:

  用快速排序的方法对数组进行排序。


实例解析:

  快速排序 (QuickSort)

  快速排序是一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。

  (1)分治法的基本思想,将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。

  (2)快速排序的基本思想

  设当前待排序的无序区为 R[low..high], 利用分治法的基本思想如下:

  ① 分解。在 R[low..high] 中任选一个记录作为基准(pivot),以此基准将当前无序区划分为左、右两个较小的子区间 R[low..pivotpos-1] 和 R[pivotpos+1..high],并使左边子区间中所有记录的关键字均小于等于基准记录(不妨记为 pivot)的关键字 pivot.key,右边的子区间中所有记录的关键字均大于等于 pivot.key,而基准记录 pivot 则位于正确的位置(pivotpos)上,无需参加后续的排序。

  划分的关键是要求出基准记录所在的位置 pivotpos。划分的结果可以简单地表示为(注意 pivot=R[pivotpos]):R[low..pivotpos-1].keys<=R[pivotpos].key<=R[pivotpos+1..high].keys,其中 low<=pivotpos<=high。

  ② 求解。通过递归调用快速排序对左、右子区间 R[low..pivotpos-1] 和 R[pivotpos+1..high] 快速排序。

  ③ 组合。因为当“求解”步骤中的两个递归调用结束时,其左、右两个子区间已有序。对快速排序而言,“组合”步骤无需做什么,可看作是空操作。

快速排序算法

void QuickSort(SeqList R,int low,int high){   // 对 R[low..high] 快速排序
  int pivotpos;                 // 划分后的基准记录的位置
  if(low<high){                 // 仅当区间长度大于 1 时才须排序
    pivotpos=Partition(R,low,high);     // 对 R[low..high] 做划分
    QuickSort(R,low,pivotpos-1);      // 对左区间递归排序
    QuickSort(R,pivotpos+1,high);      // 对右区间递归排序
  }
}//QuickSort

  为排序整个文件,调用 QuickSort(R,1,n) 即可完成对 R[1..n] 的排序。


划分算法 (Partition)

  第 1 步,(初始化)设置两个指针 i 和 j,它们的初值分别为区间的下界和上界 , 即 i=low,j=high;选取无序区的第一个记录 R[i]( 即 R[low] ) 作为基准记录,并将它保存在变量 pivot 中。

  第 2 步,令 j 自 high 起向左扫描,直到找到第 1 个关键字小于 pivot.key 的记录 R[j], 将 R[j] 移至 i 所指的位置上,这相当于 R[j] 和基准 R[i]( 即 pivot) 进行了交换,使关键字小于基准关键字 pivot.key 的记录移到了基准的左边,交换后 R[j] 中相当于是 pivot;然后令 i 指针自 i+1 位置开始向右扫描,直至找到第 1 个关键字大于 pivot.key 的记录 R[i] ,将 R[i] 移到 i 所指的位置上,这相当于交换了 R[i] 和基准 R[j],使关键字大于基准关键字的记录移到了基准的右边,交换后 R[i] 中又相当于存放了 pivot;接着令指针 j 自位置 j-1 开始向左扫描,如此交替改变扫描方向,从两端各自往中间靠扰,直至 i=j 时,i 便是基准 pivot 最终的位置,将 pivot 放在此位置上就完成了一次划分。

划分算法如下所示:

int Partition(SeqList R,int i,int j){
  //调用 Partition(R,low,high)时,对R[low..high]做划分,并返回基准记录的位置
  ReceType pivot=R[i];    // 用区间的第 1 个记录作为基础
  while(i<j){         // 从区间两端交替向中间扫描,直至 i=j 为止
    while(i<j && R[j].key>=pivot.key)  //pivot 相当于在位置 i 上
      j--;        // 从右向左扫描,查找第 1 个关键字小于 pivot.key 的记录 R[j]
    if(i<j)         // 表示找到的 R[j] 的关键字小于 pivot.key
      R[i++]=R[j];    // 相当于交换 R[i] 和 R[j], 交换后 i 指针加 1
    while(i<j && R[i].key<=pivot.key)    //pivot 相当于在位置 j 上
      i++;        // 从左向右扫描,查找第 1 个关键字大于 pivot.key 的记录 R[i]
    if(i<j)         // 表示找到了 R[i], 使 R[i].key>pivot.key
      R[j--]=R[i];    // 相当于交换 R[i] 和 R[j], 交换后 j 指针减 1
  }//endwhile
  R[i]=pivot;        // 基准记录已被最后定位
  return i;
}//Partition


程序代码—快速排序

#include <stdio.h>
#define MAX 255
int R[MAX];
int Partition(int i,int j){
  /*调用 Partition(R,low,high)时,对R[low..high]做划分,并返回基准记录的位置*/
  int pivot=R[i];      /* 用区间的第 1 条记录作为基准 */
  while(i<j){        /* 从区间两端交替向中间扫描,直至 i=j 为止 */
    while(i<j && R[j]>=pivot)    /*pivot 相当于在位置 i 上 */
      j--;        /* 从右向左扫描,查找第 1 个关键字小于 pivot.key 的记录 */
    if(i<j)         /* 表示找到的 R[j] 的关键字小于 pivot.key*/
      R[i++]=R[j];    /* 相当于交换 R[i] 和 R[j], 交换后 i 指针加 1*/
    while(i<j && R[i]<=pivot)    /*pivot 相当于在位置 j 上 */
      i++;        /*从左向右扫描,查找第 1 个关键字大于 pivot.key 的记录 R[i] */
    if(i<j)         /* 表示找到了 R[i], 使 R[i].key>pivot.key*/
      R[j--]=R[i];    /* 相当于交换 R[i] 和 R[j], 交换后 j 指针减 1*/
  }/*endwhile*/
  R[i]=pivot;        /* 基准记录已被最后定位 */
  return i;
}/*end of partition*/

void Quick_Sort(int low,int high){   /* 对 R[low..high] 快速排序 */
  int pivotpos;            /* 划分后的基准记录的位置 */
  if(low<high){            /* 仅当区间长度大于 1 时才需排序 */
    pivotpos=Partition(low,high);  /* 对 R[low..high] 做划分 */
    Quick_Sort(low,pivotpos-1);   /* 对左区间递归排序 */
    Quick_Sort(pivotpos+1,high);  /* 对右区间递归排序 */
  }
}/*end of Quick_Sort*/

void main(){
  int i,n;
  clrscr();
  puts("Please input element number of the sequence:");
  scanf("%d",&n);
  if(n<=0 || n>MAX){
    printf("n must more than 0 and less than %d.\n",MAX);
    exit(0);
  }
  puts("Please input the elements one by one:");
  for(i=1;i<=n;i++)
    scanf("%d",&R[i]);
  puts("The sequence you input is:");
  for(i=1;i<=n;i++)
    printf("%4d",R[i]);
  Quick_Sort(1,n);
  puts("\nThe sequence after quick_sort is:");
  for(i=1;i<=n;i++)
    printf("%4d",R[i]);
  puts("\n Press any key to quit..");
  getch();
}


归纳注释

  快速排序的时间主要耗费在划分操作上,对长度为 k 的区间进行划分,共需 k-1 次关键字的比较。

  最坏时间复杂度:最坏情况是每次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的结果是基准左边的子区间为空(或右边的子区间为空),而划分所得的另一个非空的子区间中记录数目,仅仅比划分前的无序区中记录个数减少一个。因此,快速排序必须做 n-1 次划分,第 i 次划分开始时区间长度为 n-i-1, 所需的比较次数为 n-i(1<=i<=n-1), 故总的比较次数达到最大值 Cmax =n(n-1)/2=O(n^2) 。如果按上面给出的划分算法,每次取当前无序区的第 1 个记录为基准,那么当文件的记录已按递增序(或递减序)排列时,每次划分所取的基准就是当前无序区中关键字最小(或最大)的记录,则快速排序所需的比较次数反而最多。

  最好时间复杂度:在最好情况下,每次划分所取的基准都是当前无序区的“中值”记录,划分的结果与基准的左、右两个无序子区间的长度大致相等。总的关键字比较次数为 O(n×lgn)。

  用递归树来分析最好情况下的比较次数更简单。因为每次划分后左、右子区间长度大致相等,故递归树的高度为 O(lgn), 而递归树每一层上各结点所对应的划分过程中所需要的关键字比较次数总和不超过 n,故整个排序过程所需要的关键字比较总次数 C(n)=O(n×lgn) 。因为快速排序的记录移动次数不大于比较的次数,所以快速排序的最坏时间复杂度应为 O(n^2 ),最好时间复杂度为 O(n×lgn)。

  基准关键字的选取:在当前无序区中选取划分的基准关键字是决定算法性能的关键。 ① “三者取中”的规则,即在当前区间里,将该区间首、尾和中间位置上的关键字比较,以三者之中值所对应的记录作为基准,在划分开始前将该基准记录和该区的第 1 个记录进行交换,此后的划分过程与上面所给的 Partition 算法完全相同。 ② 取位于 low 和 high 之间的随机数 k(low<=k<=high), 用 R[k] 作为基准;选取基准最好的方法是用一个随机函数产生一个位于 low 和 high 之间的随机数 k(low<=k<=high), 用 R[k] 作为基准 , 这相当于强迫 R[low..high] 中的记录是随机分布的。用此方法所得到的快速排序一般称为随机的快速排序。随机的快速排序与一般的快速排序算法差别很小。但随机化后,算法的性能大大提高了,尤其是对初始有序的文件,一般不可能导致最坏情况的发生。算法的随机化不仅仅适用于快速排序,也适用于其他需要数据随机分布的算法。

  平均时间复杂度:尽管快速排序的最坏时间为 O(n^2 ), 但就平均性能而言,它是基于关键字比较的内部排序算法中速度最快的,快速排序亦因此而得名。它的平均时间复杂度为 O(n×lgn)。

  空间复杂度:快速排序在系统内部需要一个栈来实现递归。若每次划分较为均匀,则其递归树的高度为 O(lgn), 故递归后所需栈空间为 O(lgn) 。最坏情况下,递归树的高度为 O(n), 所需的栈空间为 O(n) 。

  稳定性:快速排序是非稳定的。
分享到:
评论

相关推荐

    数据结构与算法程序示例。常用算法思想总结归档。使知识体系化.zip

    算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),图论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法...

    数据结构与算法分析

     本书是数据结构和算法分析的经典教材,书中使用主流的程序设计语言C++作为具体的实现语言。书的内容包括表、栈、队列、树、散列表、优先队列、排序、不相交集算法、图论算法、算法分析、算法设计、摊还分析、查找...

    数据结构与排序算法------通过代码示例,讲解:数据结构和9种排序算法。.zip

    算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),图论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法...

    算法:算法C语言实现 第1-4部分 基础知识、数据结构、排序及搜索

     7.2 快速排序算法的性能特征  7.3 栈大小  7.4 小的子文件  7.5 三者取中划分  7.6 重复关键字  7.7 字符串和向量  ……  第8章 归并与归并排序  第9章 优先队列和堆排序  第10章 基数排序  第...

    PHP四种基本排序算法示例

    但是对于基本的排序算法还是应该掌握的,它是程序开发的必备工具。这里介绍冒泡排序,插入排序,选择排序,快速排序四种基本算法,分析一下算法的思路。 前提:分别用冒泡排序法,快速排序法,选择排序法,插入排序...

    基于java语言的数据结构及算法实现,LeetCode算法示例.zip

    算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),图论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法...

    C/C++常用算法手册.秦姣华(有详细书签).rar

    4.6.2 快速排序算法示例 114 4.7 堆排序法 116 4.7.1 堆排序算法 116 4.7.2 堆排序算法示例 121 4.8 合并排序法 123 4.8.1 合并排序算法 123 4.8.2 合并排序算法示例 126 4.9 排序算法的效率 129 4.10 排序...

    数据结构与算法示例.zip

    算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),图论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法...

    学习数据结构与算法的代码示例,目前提供 Java、Python、Go、C++ 多种语言支持。.zip

    算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),图论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法...

    Java 核心类的示例、 部分工具类、 数据结构和算法.zip

    算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),图论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法...

    程序设计抽象思想:C语言描述-

     7.5 快速排序算法  7.6 数学归纳法  7.7 小结  7.8 复习题  7.9 编程练习  第Ⅲ部分 数据抽象  第8章 抽象数据类型  ……  第9章 效率与ADT  第10章 线性结构  第11章 符号表  第Ⅳ部分 递归数据  第...

    数据结构习题答案(全部算法)严蔚敏版

    6.7 二叉树的建立和遍历C语言源程序示例 习题六 第7章 图 7.1 图的基本概念和术语 7.1.1 图的基本概念 7.1.2 路径和回路 7.1.3 连通图 7.1.4 顶点的度 7.2 图的存储结构 7.2.1 邻接矩阵 7.2.2 邻接链表 ...

    《算法》中文版,Robert Sedgewick,塞奇威克

    1.4 算法分析 1.4.1 科学方法 1.4.2 观察 1.4.3 数学模型 1.4.4 增长数量级的分类 1.4.5 设计更快的算法 1.4.6 倍率实验 1.4.7 注意事项 1.4.8 处理对于输入的依赖 1.4.9 内存 1.4.10 展望 1.5 案例研究...

    《C算法》((美国)Robert Sedgewick)清晰版[DJVU] 第一卷

    4.3 栈ADT客户程序示例 99 练习 104 4.4 栈ADT实现 105 练习 108 4.5 创建新ADT 109 练习 111 4.6 FIFO队列及广义队列 111 练习 117 4.7 重复项和索引项 118 练习 121 4.8 一级ADT 122 练习 130 4.9 ADT应用示例 131...

    《C算法》((美国)Robert Sedgewick)清晰版[DJVU] 第二卷

    4.3 栈ADT客户程序示例 99 练习 104 4.4 栈ADT实现 105 练习 108 4.5 创建新ADT 109 练习 111 4.6 FIFO队列及广义队列 111 练习 117 4.7 重复项和索引项 118 练习 121 4.8 一级ADT 122 练习 130 4.9 ADT应用示例 131...

    算法,4th,塞奇威克 (Robert Sedgewick)韦恩 (Kevin Wayne), 谢路云 译.azw3

    1.4 算法分析 1.4.1 科学方法 1.4.2 观察 1.4.3 数学模型 1.4.4 增长数量级的分类 1.4.5 设计更快的算法 1.4.6 倍率实验 1.4.7 注意事项 1.4.8 处理对于输入的依赖 1.4.9 内存 1.4.10 展望 1.5 案例研究...

    数据结构与算法:语言描述(中英文)

    第14章涵盖了高级排序算法,包括流行且高效的快速排序算法。此算法是大多数在.NET框架库中实现的排序程序的基础。第15章会看到三种数据结构。在无法使用二叉查找树的时候,这三种数据结构证明对查找是很有用的。他们...

    编程珠玑 第二版 修订版

    14.4 一种排序算法 148 14.5 原理 150 14.6 习题 150 14.7 深入阅读 152 第15章 字符串 153 15.1 单词 153 15.2 短语 156 15.3 生成文本 158 15.4 原理 163 15.5 习题 163 15.6 深入阅读 164 第1版跋 165...

    《Excel应用大全》示例文件 光盘文件

    • 实现EAN-13条码的校验位的算法 • 利用文本查找函数进行模糊查找 • 利用SEARCHB 函数分离全半角字符 • 利用FIND 函数提取连续数字 • 统计开奖号码中不重复数字个数 • 取得零件规格中的最后序号 • 利用TEXT...

    并行计算导论(原书第2版).[美]Ananth Grama(带详细书签).pdf

    9.6 其他排序算法 9.6.1 枚举排序 9.6.2 基数排序 9.7 书目评注 习题 第10章 图算法 10.1 定义和表示 10.2 最小生成树:Prim算法 10.3 单源最短路径:Dijkstra算法 10.4 全部顶点对间的最短路径 10.4.1 ...

Global site tag (gtag.js) - Google Analytics