排序篇(7)--快速排序

希尔排序相当于直接插入排序的升级,它们同属于插入排序类,堆排序相当于简单选择排序的升级,同属于选择排序类,而接下来要说明的快速排序则是冒泡排序的一种升级,都属于交换排序类。

一、快速排序

基本思想:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

二、快速排序算法实现

package Sort;

/**
 * Created by LKL on 2017/3/4.
 */
public class TestQuickSort {
    public static void main(String[] args){
        int[] adj = new int[]{5,1,9,8,3,7,4,6,2};
        print(adj);
        QuickSort(adj);
    }
    public static void QuickSort(int[] adj){
        QSort(adj,0,adj.length-1);
    }
    public static void QSort(int[] adj,int low,int high){
        int pivot;
        if(low<high){
            //将序列表一分为二,算出枢轴值
            pivot = Partition(adj,low,high);
            //对低子表递归排序
            QSort(adj,low,pivot-1);
            //对高子表递归排序
            QSort(adj,pivot+1,high);
            //打印输出每次的内容
            print(adj);
        }
    }
    /*
    * 选出其中的一个关键字,放在一个合适的位置,
    * 使得左边的值都比它小,右边的值比它大,将它成为枢轴(pivot)
    *
    * */
    public static int Partition(int[] adj,int low,int high){
        int pivotkey;
        pivotkey=adj[low];
        //从表的两端交替向中间扫描
        while(low<high){
            while(low<high&&adj[high]>=pivotkey){
                high--;
            }
            swap(adj,low,high);//将比枢轴记录小的交换到低端
            while(low<high&&adj[low]<=pivotkey){
                low++;
            }
            swap(adj,low,high);//将比枢轴记录大的交换到高端
        }
        return low;
    }
    public static void swap(int[] adj,int low,int high){
        int temp;
        temp=adj[low];
        adj[low]=adj[high];
        adj[high]=temp;
    }
    public static void print(int[] data) {
        for (int i = 0; i < data.length; i++) {
            System.out.print(data[i] + "\t");
        }
        System.out.println();

    }
}

运行结果如下:

5   1   9   8   3   7   4   6   2   
1   2   3   4   5   7   8   6   9   
1   2   3   4   5   7   8   6   9   
1   2   3   4   5   6   7   8   9   
1   2   3   4   5   6   7   8   9   
1   2   3   4   5   6   7   8   9

三、快速排序优化

由于上述代码,我们是有一个假设性的操作,即假设pivot刚好处于low与high的中间,而事实并不会这么凑巧,当出现{9,1,3,5,2,4,7,6,8},此时pivot为9,转化后,并没有发生什么实质性的优化,因此有人想到了“三数取中法”,即取三个关键字先进行排序,将中间数作为枢轴,一般是取左端、右端和中间三个数。此时,pivot至少不会是最大或者最小了。
主要是修改了Patition方法,首先判定pivot不是最大,也不是最小。

 /*
    * 选出其中的一个关键字,放在一个合适的位置,
    * 使得左边的值都比它小,右边的值比它大,将它成为枢轴(pivot)
    *
    * */
    public static int Partition(int[] adj,int low,int high){
        int pivotkey;
        int m=low +(high-low)/2;
        if(adj[low]>adj[high]){
            swap(adj,low,high);
        }
        if(adj[m]>adj[high]){
            swap(adj,high,m);
        }
        if(adj[m]>adj[low]){
            swap(adj,m,low);
        }
        pivotkey=adj[low];
        //从表的两端交替向中间扫描
        while(low<high){
            while(low<high&&adj[high]>=pivotkey){
                high--;
            }
            swap(adj,low,high);//将比枢轴记录小的交换到低端
            while(low<high&&adj[low]<=pivotkey){
                low++;
            }
            swap(adj,low,high);//将比枢轴记录大的交换到高端
        }
        return low;
    }

四、快速排序复杂度分析

(1)快速排序最优的时间复杂度

快速排序最优的情况就是每一次取到的元素都刚好平分整个数组(很显然上面的不是);
此时的时间复杂度公式则为:T[n] = 2T[n/2] + f(n);T[n/2]为平分后的子数组的时间复杂度,f[n] 为平分这个数组时所花的时间;
下面来推算下,在最优的情况下快速排序时间复杂度的计算(用迭代法):

       T[n] =  2T[n/2] + n       ------第一次递归                                                                        

令:n = n/2 = 2 { 2 T[n/4] + (n/2) } + n —-第二次递归

            =  2^2 T[ n/ (2^2) ] + 2n

令:n = n/(2^2) = 2^2 { 2 T[n/ (2^3) ] + n/(2^2)} +2n ——-第三次递归

            =  2^3 T[  n/ (2^3) ]  + 3n

            ......................................................................................                        

            令:n = n/(  2^(m-1) )    =  2^m T[1]  + mn                                                  ----------------第m次递归(m次后结束)
            当最后平分的不能再平分时,也就是说把公式一直往下跌倒,到最后得到T[1]时,说明这个公式已经迭代完了(T[1]是常量了)。

得到:
T[n/ (2^m) ] = T[1] ===>> n = 2^m ====>> m = logn;
T[n] = 2^m T[1] + mn ;其中m = logn;

           T[n] = 2^(logn) T[1] + nlogn  =  n T[1] + nlogn  =  n + nlogn  ;其中n为元素个数

又因为当n >= 2时:nlogn >= n (也就是logn > 1),所以取后面的 nlogn;

           综上所述:快速排序最优的情况下时间复杂度为:O( nlogn )。
(2)最差情况下时间复杂度
最差的情况就是每一次取到的元素就是数组中最小/最大的,这种情况其实就是冒泡排序了(每一次都排好一个元素的顺序),其时间复杂度为O(n^2)。
(3)平均时间复杂度为O(nlogn)
(4)空间复杂度:
最优的情况下空间复杂度为:O(logn);每一次都平分数组的情况;
最差的情况下空间复杂度为:O( n );退化为冒泡排序的情况。

文章只是作为自己的学习笔记,借鉴了网上的许多案例,如果觉得阔以的话,希望多交流,在此谢过…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中可以使用Arrays.sort()方法对数组进行排序,但是如果需要自定义排序方式,可以使用快速排序算法来实现。快速排序是一种基于分治思想的排序算法,其基本思路是选取一个基准元素,将数组分为两部分,一部分小于基准元素,一部分大于基准元素,然后对这两部分分别进行递归排序。 以下是Java实现自定义排序方式快速排序的示例代码: ``` public class QuickSort { public static void main(String[] args) { int[] arr = {5, 2, 8, 3, 9, 1}; quickSort(arr, 0, arr.length - 1); System.out.println(Arrays.toString(arr)); } public static void quickSort(int[] arr, int left, int right) { if (left < right) { int pivot = partition(arr, left, right); quickSort(arr, left, pivot - 1); quickSort(arr, pivot + 1, right); } } public static int partition(int[] arr, int left, int right) { int pivot = arr[left]; while (left < right) { while (left < right && arr[right] >= pivot) { right--; } arr[left] = arr[right]; while (left < right && arr[left] <= pivot) { left++; } arr[right] = arr[left]; } arr[left] = pivot; return left; } } ``` 在这个示例代码中,我们定义了一个quickSort()方法来实现快速排序,其中left和right参数表示排序的范围。在quickSort()方法中,我们首先选取一个基准元素,然后调用partition()方法将数组分为两部分,一部分小于基准元素,一部分大于基准元素。然后对这两部分分别进行递归排序。 在partition()方法中,我们使用两个指针left和right来遍历数组,将小于基准元素的元素放在左边,大于基准元素的元素放在右边。最后将基准元素放在中间位置,并返回该位置。 如果需要自定义排序方式,只需要在partition()方法中修改比较的方式即可。例如,如果需要按照元素的绝对值大小进行排序,可以将pivot改为Math.abs(arr[left]),然后在while循环中使用Math.abs()方法进行比较。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值