代码改变世界

从快速排序说起

2011-05-10 22:47  sensensen  阅读(274)  评论(0编辑  收藏  举报

      排序算法是各种笔试,面试最常考到的一类题目,提到排序,一定会要求提供一种高效的方法,所以就不得不说一下快速排序了。快嘛!

写出快速排序一定要先理解什么是枢纽元(pivot),枢纽元就是每次执行快排需要参照的那个元素。

最常见的选择pivot的方法是选择第一个元素。此外还有最后一个元素,随机选择,中值法等等。

比如 20,34,4,53,43,42,6,67,193  选择20为pivot经过一趟快速排序后会达到:

       6,4,20,53,43,42,34,67,193  过程可参见动画http://www.jcc.jx.cn/xinwen3/news/kj/flash/2004/0426/1306.htm

由此我们也可以看到快速排序每次的目的就是找到枢纽元的准确位置,

即 小于枢纽元的数 | 枢纽元 | 大于等于枢纽元的数

所以只要你能每趟排序达到这样的效果,就可以条条大陆通罗马了。

实现快速的排序的难点在于, 

(1) 如何停止递归,即什么时候return? 
 比较容易记忆的方式是:在 start>=end 时 return,很明显这时表示待排序的序列只有一个或0个元素。不需要做任何处理,所以返回。

 (2) i,j越界问题。

      编写程序时,只要单独考虑到以下三条特例情况,如果不出问题通常可以满足越界要求了:

      1.  2个元素的排序,

      2.  i++到end时,

      3.  j--到为-1时。

 (3) 如何停止i++,j--操作,即什么时候算是完成一趟快速排序操作?

 通常i++,j--外面要包一层循环,问题也就变成了,如何停止这个循环。

 比较常见的做法是:

             do{

             }

             while(i<j)

 当然也可用:

             for(; ;)

             {

                if (i < j)

                {...}

                else

                  break;

             }

 (4) 如果序列有相等元素怎么办?

 一种常见方法是,对于j--操作要求判断条件为 <= 而不是<

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/midgard/archive/2009/04/09/4060027.aspx

先贴出自己的实现代码吧:

1 #include "stdafx.h"
2  #define MAX 50
3  void swap(int *a,int *b)
4 {
5 int temp;
6 temp=*a;
7 *a=*b;
8 *b=temp;
9 }
10  int sort(int a[],int low,int high)
11 {
12 int temp=a[low];
13 int i=low,j=high;
14 while(i<j){
15 for(;i<=j;j--)
16 {
17 if(a[j]<temp)
18 {
19 swap(&a[j],&a[i]);
20 break;
21 }
22 }
23 for(;i<j;i++){
24 if(a[i]>temp) {
25 swap(&a[i],&a[j]);
26 break;
27 }
28 }
29 }
30 return i;
31 }
32  void quicksort(int a[],int low,int high)
33 {
34 int mark;
35 if(low<high){
36 mark=sort(a,low,high);
37 quicksort(a,low,mark-1);
38 quicksort(a,mark+1,high);
39 }
40 }
41
42 void _tmain(int argc, _TCHAR* argv[])
43 {
44 //int a[11]={0,11,12,5,6,13,8,9,14,7,10};
45 int num=12;
46 int a[MAX];
47 while(1){
48 for(int i=0;i<num;i++)
49 scanf("%d",&a[i]); //在这里加入了输入,当输入为09时视作9.注意,这里如果
50 //有一次输入的值多于num则可能后面一次继续用前面没输入进来的输入值
51 for(int i=0;i<num;printf("%3d",a[i]),i++);
52 printf("\n");
53 quicksort(a,0,num-1);
54 for(int i=0;i<num;printf("%3d",a[i]),i++);
55 printf("\n");
56 getchar();
57 }
58 /*return 0;*/
59 }

这里有几层函数还是自己要事先考虑清楚的,不然会总觉得解决不了问题。一句话,纸上学得终觉浅~~要躬行啦~~

以上的程序是比较简单的,用到了swap,应该说效率也不是很高,不过是最基本也是最不容易出错的做法。更高效优化的做法是利用相互的交换位置。

更好的做法是:sort里面的部分改为以下部分:

while(i<j){
while(i<j&&a[j]>=temp)
--j;
a[i]
=a[j];
while(i<j&&a[i]<=temp)//这里必须要有等于号,否则在当有重复出现的数字的时候会出错。
++i;
a[j]
=a[i];
}

关于枢纽元的选择就不想多说了,最简单的是选择第一个值或者最后一个值,这种对于一般随机的是有用的,但是对于逆序或其他情况不大好,比较好的方法是选择首、末、中间的三个值,选他们的中值作为枢纽元。

下面这个呢,是网上贴的,没验证过,应该没啥问题吧

void qsort(int s[], int l, int r)
{
    int i, j, x;
    if (l < r)
    {
        i = l;
        j = r;
        x = s[i];
        while (i < j)
        {
            while(i < j && s[j] > x) j--; /* 从右向左找第一个小于x的数 */
            if(i < j) s[i++] = s[j];
            while(i < j && s[i] < x) i++; /* 从左向右找第一个大于x的数 */
            if(i < j) s[j--] = s[i];
        }
        s[i] = x;
        qsort(s, l, i-1); /* 递归调用 */
        qsort(s, i+1, r);
    }
}