快速排序和三向快速排序

快速排序

快速排序是一种分治的排序算法。它将一个数组分成两个子数组,将两部分独立的排序。快速排序和归并排序是互补的:归并排序将数组分成两个子数组分别排序,并将有序的子数组归并以将整个数组排序;而快速排序将数组排序的方式则是当两个子数组都有序时整个数组也就自然有序了。在归并排序中,递归调用发生在处理整个数组之前,而快速排序中,递归调用发生在处理整个数组之后。

快速排序算法是最快的通用排序算法,大部分情况下,可以直接选择快速排序

如上图所示,将第一个元素K作为切分元素,使得左边的元素均不大于K,右边的元素均不小于K,这样如果左右两边都有序了,那么整个数组也就有序了。

这就是快速排序的基本原理,如果看过我前一篇关于归并排序的博客,那么很容易理解,这里需要用到递归。

先上代码:

}

    public void quickSort(Integer[] a,Integer low,Integer high) {

        if(low >= high)

            return;

        Integer j = partion(a,low,high);

        quickSort(a,low,j);

        quickSort(a,j+1,high);

    }    

这是递归调用的代码,我们先不管partion函数,现在只需要知道这个函数返回切分元素的下表,如上面图示的那样,返回K元素所在的下标,那么就将数组分成两个数组:

  1. 所有小于K的元素的数组A
  2. 所有大于K的元素的数组B
  3. 同样在A中找到一个切分元素将A分成两个子数组A‘
  4. 同样在B中找到一个切分元素将A分成两个子数组B‘
  5. 一直递归下去,直到不能再切分
  6. 当最小切分的数组有序时,再一次往回递归排序,这样最后整个数组就有序了。

说的有些乱,但是其实还是很好理解的。下面给出partion函数的代码:

    public Integer partion(Integer[] a,Integer low,Integer high) {

        Integer i = low;

        Integer j = high + 1;

        while(true) {

            //在循环中最好使用++i,如果使用i++,则会导致很多逻辑错误,i++是在循环结束时(即运行到大括号时)才会++,这也是为什么前面需要使用high+1

            while(a[++i]<a[low])

                if(i == high)

                    break;

            while(a[--j]>a[low])

                if(j == low)

                    break;

            if(i >= j)

                break;

            change(a,i,j);

        }

        change(a,low,j);

        System.out.println("low: " + low);

        return j;

    }

这个函数的作用是可以用下图说明:

1、以第一个元素为切分元素

2、从low往右找到第一个大于a[low]的元素

3、从high往左找到第一个小于a[high]的元素

4、交换a[low]和a[high]

5、重复2-4,

6、最后就得到如上图第三幅图所示的结果。

使得第一个切分元素v左边的元素全部小于v,右边的元素全部大于v,并返回v的下标j。

完整代码如下:

public class QuickSort extends SortBase {

    /* (non-Javadoc)

     * @see Sort.SortBase#sort(java.lang.Integer[])

     */

    @Override

    public Integer[] sort(Integer[] a) {

        print("init",a);

        quickSort(a,0,a.length-1);

        print("result",a);

        return a;

    }

    public void quickSort(Integer[] a,Integer low,Integer high) {

        if(low >= high)

            return;

        Integer j = partion(a,low,high);

        quickSort(a,low,j);

        quickSort(a,j+1,high);

    }

    public Integer partion(Integer[] a,Integer low,Integer high) {

        Integer i = low;

        Integer j = high + 1;

        while(true) {

            //在循环中最好使用++i,如果使用i++,则会导致很多逻辑错误,i++是在循环结束时(即运行到大括号时)才会++,这也是为什么前面需要使用high+1

            while(a[++i]<a[low])

                if(i == high)

                    break;

            while(a[--j]>a[low])

                if(j == low)

                    break;

            if(i >= j)

                break;

            change(a,i,j);

        }

        change(a,low,j);

        System.out.println("low: " + low);

        return j;

    }

    public static void main(String[] args) {

        Integer[] a = {2,1,5,9,0,6,8,7,3};

        (new QuickSort()).sort(a);

    }

}

平均时间复杂度NlogN

三向快速排序

实际情况中经常会出现含有大量重复元素的数组,例如我们可能需要将大量人员资料按照生日排序,或是按照性别区分。在这些情况下,快速排序算法性能尚可,但还有巨大的改进空间,这就是三向快速排序。

简单的说,三向快速排序的原理为:将数组切分成三部分们分别对应于小于、等于、

大于切分元素的数组元素。与快速排序相比,增加了一个等于切分元素的区域。

流程如下:

从做到右遍历数组一次,维护一个指针lt使得a[low…lt-1]中的元素都小于v,一个指针gt使得a[gt+1…high]中的元素都大于 v,一个指针i使得a[lt…i-1]中的元素都等于v,a[i…gt]中的元素都还为确定,一开始i和lo相等。

使得i递增,对于a[i]:

a[i]小于v,将a[lt]和a[i]交换,将lt和i加一

a[i]大于v,将a[gt]和a[i]交换,将gt减一

a[i]等于v,将i加一

最后使数组呈现下图中的情况:

实现代码:

    public void quickSort3Way(Integer[] a,Integer low,Integer high) {

        if(low >= high)

            return;

        Integer lt = low;

        Integer i = low + 1;

        Integer gt = high;

        while(i<=gt) {

            if(a[i] < a[lt]) {

                change(a,i,lt);

                i++;

                lt++;

            } else if(a[i] > a[lt]) {

                change(a,i,gt);

                //这里不能使用i--,因为交换a[gt]和a[i]后,现在的a[i]并没有确定位置,如果使用i++,就将跳过交换后该元素的排序

                gt--;

            } else {

                i++;

            }

            print("a",a);

        }

        quickSort3Way(a,low,lt-1);

        quickSort3Way(a,gt+1,high);

    }

运行结果:

init: [2 ,1 ,5 ,9 ,0 ,6 ,8 ,7 ,3]

a: [1 ,2 ,5 ,9 ,0 ,6 ,8 ,7 ,3]

a: [1 ,2 ,3 ,9 ,0 ,6 ,8 ,7 ,5]

a: [1 ,2 ,7 ,9 ,0 ,6 ,8 ,3 ,5]

a: [1 ,2 ,8 ,9 ,0 ,6 ,7 ,3 ,5]

a: [1 ,2 ,6 ,9 ,0 ,8 ,7 ,3 ,5]

a: [1 ,2 ,0 ,9 ,6 ,8 ,7 ,3 ,5]

a: [1 ,0 ,2 ,9 ,6 ,8 ,7 ,3 ,5]

a: [1 ,0 ,2 ,9 ,6 ,8 ,7 ,3 ,5]

a: [0 ,1 ,2 ,9 ,6 ,8 ,7 ,3 ,5]

a: [0 ,1 ,2 ,6 ,9 ,8 ,7 ,3 ,5]

a: [0 ,1 ,2 ,6 ,8 ,9 ,7 ,3 ,5]

a: [0 ,1 ,2 ,6 ,8 ,7 ,9 ,3 ,5]

a: [0 ,1 ,2 ,6 ,8 ,7 ,3 ,9 ,5]

a: [0 ,1 ,2 ,6 ,8 ,7 ,3 ,5 ,9]

a: [0 ,1 ,2 ,6 ,5 ,7 ,3 ,8 ,9]

a: [0 ,1 ,2 ,5 ,6 ,7 ,3 ,8 ,9]

a: [0 ,1 ,2 ,5 ,6 ,3 ,7 ,8 ,9]

a: [0 ,1 ,2 ,5 ,3 ,6 ,7 ,8 ,9]

a: [0 ,1 ,2 ,3 ,5 ,6 ,7 ,8 ,9]

a: [0 ,1 ,2 ,3 ,5 ,6 ,7 ,8 ,9]

result: [0 ,1 ,2 ,3 ,5 ,6 ,7 ,8 ,9]

平均时间复杂度:介于N和NlogN之间

时间: 2024-08-29 13:43:42

快速排序和三向快速排序的相关文章

三种快速排序以及快速排序的优化

一.  快速排序的基本思想 快速排序使用分治的思想,通过一趟排序将待排序列分割成两部分,其中一部分记录的关键字均比另一部分记录的关键字小.之后分别对这两部分记录继续进行排序,以达到整个序列有序的目的. 二.  快速排序的三个步骤 1) 选择基准:在待排序列中,按照某种方式挑出一个元素,作为 "基准"(pivot): 2) 分割操作:以该基准在序列中的实际位置,把序列分成两个子序列.此时,在基准左边的元素都比该基准小,在基准右边的元素都比基准大: 3) 递归地对两个序列进行快速排序,直到

【转】三种快速排序算法的实现(递归算法、非递归算法、三路划分快速排序)

原文:http://blog.csdn.net/left_la/article/details/8206405 快速排序的三个步骤: 1.分解:将数组A[l...r]划分成两个(可能空)子数组A[l...p-1]和A[p+1...r],使得A[l...p-1]中的每个元素都小于等于A(p),而且,小于等于A[p+1...r]中的元素.下标p也在这个划分过程中计算. 2.解决:通过递归调用快速排序,对数组A[l...p-1]和A[p+1...r]排序. 3.合并:因为两个子数组时就地排序,将它们的

三种快速排序法

/*交换函数:为了提高效率,当所交换的两个元素值不相等时,用异或运算*/ void swap(int *a, int *b) { if (*a != *b){ *a = *a^*b; *b = *a^*b; *a = *a^*b; } else{ int temp = *a; *a = *b; *b = temp; } } /*第一种快排:只有一个长度n,每次需计算出low和high指针*/ int QuickSort_process1(int *a, int n) { int low, hig

【转】三种快速排序算法以及快速排序的优化

一.  快速排序的基本思想 快速排序使用分治的思想,通过一趟排序将待排序列分割成两部分,其中一部分记录的关键字均比另一部分记录的关键字小.之后分别对这两部分记录继续进行排序,以达到整个序列有序的目的. 二.  快速排序的三个步骤 1) 选择基准:在待排序列中,按照某种方式挑出一个元素,作为 “基准”(pivot): 2) 分割操作:以该基准在序列中的实际位置,把序列分成两个子序列.此时,在基准左边的元素都比该基准小,在基准右边的元素都比基准大: 3) 递归地对两个序列进行快速排序,直到序列为空或

小橙书阅读指南(六)——快速排序和三向切分快速排序

算法描述:快速排序是一种分治的排序算法.它将数组分为两个子数组,并将两部分独立的排列.快速排序和归并排序是互补的:归并排序将数组分成两个子数组分别排序,并将子数组归并以将整个数组排序:而快速排序将数组排序的方式则是当两个子数组都有序时整个数组也就自然有序了. 算法图示: 算法解释:选择标的元素(5)并且便利数组,将素有小于5的元素都安排在它的左侧,而大于5的元素都安排在它的右侧.之后再通过递归的方法分别处理左边的子数组和右边的子数组. 快速排序的算法难点在于尽量不要使用额外的存储空间(即保证原地

排序算法三:快速排序

一.算法思想 1.快速排序基本思想是:分治思想:即将一个大的问题通过:分解--解决--合并,这几步,从而实现排序.一般是将大问题划分成很多个一样的问题,然后递归的解决每个小问题.最后,将每个小问题解决的结果合并起来就解决了问题. 2.基本过程:对于一个无序的序列,首先,选择一个关键元素,作为划分元素的分界线:一般选择第一个或者最后一个元素.然后,遍历数组,将小于关键元素的放到它的左边,大于关键元素的放到他的右边,最后将关键元素插入到正确位置.然后,按照相同的处理方法,划分左右两部分的内容.最后,

常用算法之排序算法三【快速排序】

快速排序是东尼·霍尔在1962提出的划分交换排序,并采用一种分治的策略.在这,我们先总结一下:快速排序 = 划分交换排序 + 分治.然后我们在一一介绍他. 划分交换排序 在讨论它时,感觉有种看了崔颢<黄鹤楼>之后,再去写黄鹤楼的感觉,因为MoreWindows写 得白话经典算法系列之六 快速排序 快速搞定已经足够出色了.我在这只是进行简单的复述,你需要了解更多请看他的博文. 先来看看划分交换排序的具体算法描述: 1.从数列中选出一个数作为基准 2.从数组中选出比它大的数放右边,比它小的数放左边

数据结构值排序算法(三)-快速排序

基本思想: 快速排序采用的思想是分治思想. 第一趟排序:快速排序是找出一个元素(理论上可以随便找一个)作为基准(pivot),然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置. 递归排序:第二步就是对高段位和地段为两部分进行递归排序. 一趟快速排序的算法的步骤是: 1)设置两个变量low.high,排序开始的时候:low=0,high=N-1: 2)以第一个数组元素作为关键数据,赋值给key,即key=A[0]:

算法导论(三) 快速排序

快排 直接上代码,数学推导以后再上 还有一版随机化版本的快速排序 #include <iostream> using namespace std; void _swap(int a[] , int i , int j)//交换函数 { int temp; temp = a[i]; a[i] = a[j]; a[j] = temp; } int Partition(int *a,int p,int r)//划分程序 { int x,i; x=a[r]; i=p-1; for(int j=p;j&