现在的位置: 首页 > 综合 > 正文

快速排序算法

2013年10月30日 ⁄ 综合 ⁄ 共 4134字 ⁄ 字号 评论关闭
设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将值为key的项与A[j]交换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将值为key的项与A[i]交换;
5)重复第3步
6)重复第3、4、5步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[j]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。
排序演示
假设用户输入了如下数组:
下标
0
1
2
3
4
5
数据
6
5
7
3
8
9
创建变量i=0(指向第一个数据), j=5(指向最后一个数据), k=6(赋值为第一个数据的值)。
我们取走了下标0的数据,于是,我们需要找到一个数字来替换他。由于我们要把所有比6小的数移动到左面,所以我们可以开始寻找比6小的数啦。别急,我们要按顺序找哦。不断递减j的值,我们发现下标3的数据比6小,于是把3移到下标0(实际是i指向的位置。代码中要用i,因为后面还会循环这个步骤,不用i的话第二次循环就会出问题。),数组和变量变成了以下状态:
下标
0
1
2
3
4
5
数据
3
5
7
3
8
9
i=0 j=3 k=6
由于变量k已经储存了下标0的数据,所以我们可以放心的把下标0覆盖了。如此一来,下标3虽然有数据,但是相当于没有了,因为数据已经复制到别的地方了。于是我们再找一个数据来替换他。这次要变成找比k大的了,而且要从前往后找了。递加变量i,发现下标2是第一个比k大的,于是用下标2的数据7替换j指向的下标3的数据,数据状态变成下表:
下标
0
1
2
3
4
5
数据
3
5
7
7
8
9
i=2 j=3 k=6
重复上面的步骤,递减变量j。这时,我们发现i和j“碰头”了:他们都指向了下标2。于是,循环结束,把k填回下标2里,即得到结果。
如果i和j没有碰头的话,就递加i找大的,还没有,就再递减j找小的,如此反复,不断循环。注意判断和寻找是同时进行的。
注意:这里的数据给的不太好,实际上这样排序以后不会得到最终结果,只会把比k大和比k小的数分到k的两边。(你可以想象一下i和j是两个机器人,数据就是大小不一的石头,先取走i前面的石头留出回旋的空间,然后他们轮流分别挑选比k大和比k小的石头扔给对面,最后在他们中间把取走的那块石头放回去,于是比这块石头大的全扔给了j那一边,小的全扔给了i那一边。只是这次运气好,扔完一次刚好排整齐。)为了得到最后结果,需要再次对下标2两边的数组分别执行此步骤,然后再分解数组,直到数组不能再分解为止(只有一个数据),才能得到正确结果。

java实现

public static void quickSort(int a[], int start, int end)
{        int i,j;
         i = start;
         j = end;
         if((a==null)||(a.length==0))
             return;
         while(i<j){
             while(i<j&&a[i]<=a[j])     //以数组start下标的数据为key,右侧扫描
             j--;
             if(i<j){                   //右侧扫描,找出第一个比key小的,交换位置
                 int temp = a[i];
                 a[i] = a[j];
                 a[j] = temp;
             }
             while(i<j&&a[i]<a[j])    //左侧扫描(此时a[j]中存储着key值)
                 i++;
             if(i<j){                 //找出第一个比key大的,交换位置
                 int temp = a[i];
                 a[i] = a[j];
                 a[j] = temp;
             }
        }
        if(i-start>1){
             //递归调用,把key前面的完成排序
            quickSort(a,0,i-1);
        }
        if(end-j>1){
            quickSort(a,j+1,end);    //递归调用,把key后面的完成排序
        }
}
//////////////////////////// 方式二 ////////////////////////////////
更有效率点的代码:
public <T extends Comparable<? super T>> T[] quickSort(T [] targetArr,
            int start, int end) {
       int i = start + 1, j = end;
       T  key = targetArr[start];
       SortUtil<T> sUtil = new SortUtil<T>();
     
       if (start >= end) {
            return targetArr;
        }
     
       /* 从i++和j--两个方向搜索不满足条件的值并交换  *
        * 条件为:i++方向小于key,j--方向大于key      */
       while (true) {
            while (targetArr[j].compareTo(key) > 0) {
                j --;
            }
            while (targetArr[i].compareTo(key) < 0 && i < j) {
                i ++;
            }
            if(i >= j) {
                break;
            }
            sUtil.swap(targetArr, i, j);
            if(targetArr[i] == key) {
                j--;
            } else {
                i++;
            }
       }
     
       /* 关键数据放到‘中间’ */
       sUtil.swap(targetArr, start, j);
     
       if(start  < i - 1) {
            this.quickSort(targetArr, start, i - 1);
        }
       if(j + 1 < end) {
           this.quickSort(targetArr, j + 1 , end);
        }
       
        return targetArr;
    }

////////////////方式三:减少交换次数,提高效率/////////////////////
private <T extends Comparable<? super T>> void quickSort(T[] targetArr,
             int start, int end) {
        int i =  start, j = end;
        T key = targetArr[start];
        
        while(i < j) {
            //按 j -- 方向遍历目标数组,直到比 key 小的值为止
            while(j > i && targetArr[j].compareTo(key) >= 0) {
                j --;
            }
            if(i < j) {
                // targetArr[i] 已经保存在key中,可将后面的数填入
                targetArr[i] = targetArr[j];
            }
            //按 i ++ 方向遍历目标数组,直到比 key 大的值为止
            while(i < j && targetArr[i].compareTo(key) < 0) {
                i ++;
            }
            if(i < j) {
                // targetArr[j] 已保存在targetArr[i]中,可将前面的值填入
                targetArr[j] = targetArr[i];
            }
        }
        // 此时 i == j
        targetArr[i] = key;
        
        if(i - start > 1) {
            // 递归调用,把key前面的完成排序
            this.quickSort(targetArr, start, i - 1);
        }
        if(end - j > 1) {
            // 递归调用,把key后面的完成排序
            this.quickSort(targetArr, j + 1, end);
        }
    }


运行结果:27 38 13 49 76 97 65快速排序就是递归调用此过程——在以49为中点分割这个数据序列,分别对前面一部分和后面一部分进行类似的快速排序,从而完成全部数据序列的快速排序,最后把此数据序列变成一个有序的序列,根据这种思想对于上述数组A的快速排序的全过程如图6所示:初始状态 {49 38 65 97 76 13 27} 进行一次快速排序之后划分为 {27 38 13} 49 {76 97 65} 分别对前后两部分进行快速排序{27 38 13} 经第三步和第四步交换后变成 {13 27 38} 完成排序。{76 97 65} 经第三步和第四步交换后变成 {65 76 97} 完成排序。图示

抱歉!评论已关闭.