zoukankan      html  css  js  c++  java
  • 算法练习——快速排序

     主要参考资料 : 算法导论

    1:时间复杂度介绍

    对于包含n个数的输入数组来说,快速排序是一种最坏情况时间复杂度为O(n2)的排序算法.虽然最坏情况时间复杂度很差,但是快速排序通常是实际排序应用中最好的选择,因为它的平均性能非常好:它的期望时间复杂度是O(nlgn).而且O(nlgn)中隐含的常数因子非常小。另外,它还能够进行原址排序,甚至在虚存环境中也能很好地工作。

    2:基本思路

    (1)分解:

    数组A[p ... r]被划分为两个(可能为空)子数组A[p...q-l]和A[q+1 ... r],使得A[p ... q-l]中的每一个元素都小于等于A[q]
    而A[q]也小于等于A[q+1...r]中的每个元素。其中,计算下标 q 也是划分过程的一部分。

    (2)解决:通过递归调用快速排序,对子数组A[p ... q-l]和A[q+1...r] 进行排序.
    (3)合并:因为子数组都是原址排序的,所以不需要合并操作:数组A[p...r]已经有序.

     

     下面给出快速排序的核心代码分析:

    如何实现上面的数组划分过程:

     1 //定义函数用于主要的划分 p表示数组的下标,r表示数组的上标
     2     public int partition(int[] a ,int p, int r){
     3         int x = a[r];
     4         int i = p-1;
     5         for (int j = p; j <= r-1; j++) {
     6             if (a[j] <= x) {
     7                 i++;
     8                 swap(a,i,j);
     9             }
    10         }
    11         swap(a, i+1, r);
    12         return i+1;
    13     }

    下面分析如何对数组进行划分:

    (1)首先选择一个  x = A[r] 元素作为主元 (或者“哨兵”)围绕该元素进行划分数组。小于x   放在一起,大于 x 的放在一起。

    (2)对于5-10行  循环体的每一轮迭代开始时,对于任意教组下标k,有:

    1.若 p ≤  k ≤  i,则A[k]  ≤  x。
    2.若 i+1 ≤ k ≤  j-l,则A[k ] >  x。
    3.若 k = r,则 A[k]  = x。

     

    上图表示PARTITION操作过程.数组项A[r]是主元x。浅阴影部分的数组元素都在划分的第一部分,其值都不大于x
    深阴影部分的元素都在划分的第二部分,其值都大于x.无阴影的元素则是还未分人这两个部分中的任意一个。最后的白色元素就是主元x。
    (a)初始的数组和变最设置.数组元素均未被放人前两个部分中的任何一个.
    (b) 2与它自身进行交换,并被放入了元素值较小的那个部分。
    (c)~(d)8和7被添加到元素值较大的那个部分中.
    (e)l和8进行交换,数值较小的部分规模增加.
    (f)数值3和7进行交换,数值较小的部分规模增加.
    (g)~(h)5和6被包含进较大部分,循环结束.
    (i)在第7~8行中,主元被交换,这样主元就位于两个部分之间

     

    然后分别递归调用   quickSort(A ,  p, q-1 )  以及   quickSort(A , q+1, r)

     具体的代码实现如下:

     1 package com.hone.heapsort;
     2 
     3 
     4 public class QucikSort {
     5     
     6     //这里面使用一个变量q怎么解决执行第一个递归的时候造成的q值的变化,不在
     7     //等于第一次返回的中值
     8     public void quickSort(int[] A,int m,int n){
     9         if(m < n){
    10             int q = partition(A, m, n);
    11             quickSort(A, m, q-1);
    12             quickSort(A, q+1, n);
    13         }
    14     }
    15     
    16     
    17     //定义函数用于主要的划分 p表示数组的下标,r表示数组的上标
    18     public int partition(int[] a ,int p, int r){
    19         int x = a[r];
    20         int i = p-1;
    21         for (int j = p; j <= r-1; j++) {
    22             if (a[j] <= x) {
    23                 i++;
    24                 swap(a,i,j);
    25             }
    26         }
    27         swap(a, i+1, r);
    28         return i+1;
    29     }
    30 
    31     private void swap(int[] a, int i, int j) {
    32         int temp = a[j];
    33         a[j] = a[i];
    34         a[i] = temp;
    35     }
    36 
    37     public void printMy(int[] a){
    38         for (int i = 0; i < a.length; i++) 
    39             System.out.print(a[i]+" ");
    40         System.out.println();
    41     }
    42     
    43     public static void main(String[] args) {
    44         int[] a = {2,8,7,1,3,5,6,4};
    45         int m = 0;
    46         int n = a.length-1;
    47         QucikSort qsort = new QucikSort();
    48         System.out.print("原数组: ");
    49         qsort.printMy(a);
    50         qsort.quickSort(a, m, n);
    51         System.out.print("排序后: ");
    52         qsort.printMy(a);
    53     }
    54 }

    4:时间复杂度分析:

    最坏情况:当每次划分都产生  n-1  个元素  以及  0个元素的时候,最坏情况就产生了。

    T(n)  =  T(n-1)+T(0)+O(n)

    也就是说,如果算法每次递归一次的时候都产生极大的不平衡性,则算法的最坏情况就产生了,并且时间复杂度为  O(n2)。

    最好情况:在可能的最平衡的划分中,PARTITION得到的两个子问题的规模都不大于n/2。在这种情况下,快速排序的性能非常好.此时,算法运行时间的递归式为:

                            T(n) = 2T(n/2)+O(n)
         在上式中,可以得到时间复杂度为 O(nlogn)

    并且在一般情况下,之后每次二分的比例为常数倍,算法的时间复杂度总是为   O(nlogn)

  • 相关阅读:
    Maven简介,安装,配置
    Centos 安装 Tomcat 并验证
    Centos 安装 jdk 和配置环境变量
    java基本数据类型
    centos7中docker安装并启动jpress
    在docker中访问网络
    在docker中运行一个nginx
    Centos7 安装docker 及使用其简单命令
    Centos7安装dnf工具管理rpm包
    C#获取外网IP地址;C#获取所在IP城市地址
  • 原文地址:https://www.cnblogs.com/xiaxj/p/7416650.html
Copyright © 2011-2022 走看看