快速排序的最坏情况基于每次划分对主元的选择。基本的快速排序选取第一个元素作为主元。这样在数组已经有序的情况下,每次划分将得到最坏的结果。一种比较常见的优化方法是随机化算法,即随机选取一个元素作为主元。这种情况下虽然最坏情况仍然是O(n^2),但最坏情况不再依赖于输入数据,而是由于随机函数取值不佳。实际上,随机化快速排序得到理论最坏情况的可能性仅为1/(2^n)。所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望时间复杂度。一位前辈做出了一个精辟的总结:“随机化快速排序可以满足一个人一辈子的人品需求。”

  随机化快速排序的唯一缺点在于,一旦输入数据中有很多的相同数据,随机化的效果将直接减弱。对于极限情况,即对于n个相同的数排序,随机化快速排序的时间复杂度将毫无疑问的降低到O(n^2)。

快速排序的实现需要消耗递归栈的空间,而大多数情况下都会通过使用系统递归栈来完成递归求解。在元素数量较大时,对系统栈的频繁存取会影响到排序的效率。

  一种常见的办法是设置一个阈值,在每次递归求解中,如果元素总数不足这个阈值,则放弃快速排序,调用一个简单的排序过程完成该子序列的排序。这样的方法减少了对系统递归栈的频繁存取,节省了时间的消费。

  一般的经验表明,阈值取一个较小的值,排序算法采用选择、插入等紧凑、简洁的排序。一个可以参考的具体方案:阈值T=10,排序算法用选择排序。

  阈值不要太大,否则省下的存取系统栈的时间,将会被简单排序算法较多的时间花费所抵消。

  另一个可以参考的方法,是自行建栈模拟递归过程。但实际经验表明,收效明显不如设置阈值。

 

以下是C语言权威《The C Programming Language》中的例程,在这个例程中,对于数组v的left到right号元素以递增顺序排序。

  //Qsort.c by Tydus.

  #include <stdio.h>

  int arr[] = {14,10,11,5,6,15,0,15,16,14,0,8,17,15,7,19,17,1,18,7};

  /* swap函数:交换v[k]与v[j]的值 */

  inline void swap(int v[], int k, int j)

  {

  int temp;

  temp = v[k];

  v[k] = v[j];

  v[j] = temp;

  }

  void qsort(int v[], int left, int right)

  {

  int j, last;

  if (left >= right) /* 若数组包含的元素个数少于两个 */

  return; /* 则不执行任何操作 */

  swap(v, left, (left + right)/2); /* 将划分子集的元素移动到V[0] */

  last=left; /* 用last记录中比关键字小间的最右位置*/

  for (j = left+1; j <= right; j++) /* 划分子集 */

  {

  if (v[j] < v[left])

  {

  swap(v, ++last, j);

  }

  }

  /*通过上述过程会形成 关键字(中left所在位置) 小小小...(last所在位置)大大大大(最后)*/

  swap(v, left, last); /* 恢复划分的元素 */

  /*小小。。。。关键字大大大大*/

  qsort(v, left, last-1);

  qsort(v, last+1, right);

  }

  void main()

  {

  int j;

  qsort(arr, 0, 19);

  for(j=0; j<=19; j++)

  {

  printf("%d ", arr[j]);

  }

  printf("\n");

  }

 

传统的快速排序是递归的,这就会受到递归栈深度的限制。比如在一台普通的PC上,当待排序元素达到10^6以上时,传统的递归快排会导致栈溢出异常,或者一个莫名其妙的错误结果。所以,对于巨大的数据规模,将快速排序消除递归是十分必要的。而消除递归,又将带来巨大的性能提升,把系统级的消耗降到最低。

  消除递归的方法,就是模拟栈操作。但是从代码可以看出,这种模拟的消耗几乎可以忽略不计。因此消除递归的快排的效率是有保障的。

  (虽然下面的代码没有使用随机化,但经过测试,它是目前所有快排编写方法中,效率最高,速度最快的!)

  ////////////////////////////////////////////////////////////////////////////////

  #define MAXARRAY 10000

  #define PUSH(A,B) {sl[sp]=A;sr[sp]=B;sp++;}

  #define POP(A,B) {sp--;A=sl[sp];B=sr[sp];}

  void quicksort(int a[],int l,int r){

  static int sl[MAXARRAY], sr[MAXARRAY], sp;

  int i,j,p,t;

  sp=0;

  PUSH(l,r);

  while(sp){

  POP(l,r);

  i=l;j=r;p=a[(i+j)/2];

  while(i<=j){//2.建议改成:while(i<j){

  while(a<p)i++;//应为while(a<p)i++;

  while(a[j]>p)j--;

  if(i<=j){//3.建议改成:if(i<j){

  t=a;a=a[j];a[j]=t;//应为t=a;a=a[j];a[j]=t;

  i++;j--;//1.建议注释掉//不能注释掉!

  }

  }

  if(l<j)PUSH(l,j);

  if(i<r)PUSH(i,r);

  }

  }
http://baike.baidu.com/view/115472.htm

posted on 2009-04-08 12:20  AlexusLi  阅读(1819)  评论(0编辑  收藏  举报