Java 快速排序

来源:互联网 发布:手机连不上4g网络 编辑:程序博客网 时间:2024/05/17 04:37

快速排序作为一种快速的排序算法,所以现在就详细的介绍一下 。

快速排序是冒泡的一种改进 都是交换类的排序算法

( 冒泡排序是每轮都把当前排序范围中最大的数放到最后 )


快速排序是确定一个PivotKey ,把当前数组范围分成三份,左边是小于PivotKey的,中间是PivotKey,右边是大于PivotKey的

当然可能PivotKey最小 没有左边的部分 ,反之。


快速排序是基于分治策略的一种算法    (分治法的思想是将一个规模为n 的问题分解成k个较小规模的子问题,子问题互相独立且与原问题相同)


快速排序的基本思想是三个步奏:

 1,分解 : 通常以待排列数组第一个元素为 PivotKet(或用随机选择策略 随机选取其中一个元素作为PivotKey), 将数组划分成三段 , 中间是小于PivotKey的元素
 中间是PivotKey 右侧是大于它的元素 (要是PivotKey为最小的元素 就直接去第二步)

 2,递归求解:通过递归手段 对上一步的左边 和 右边的数组进行快速排序

 3,合并 ;由于对于子数组的排列是直接用的原数组,所以在递归结束,整个数组已经是有序的了 不需要额外的合并操作 


上代码,这里的代码只是一种解法  别的解法暂无研究 ,代码有错误  或者不完美的留言指正,你说我就改,就怕你不来


public class Test {private static int[] data;/** * 对 数组 s 到 e 这一段进行快速排序 步骤如下: 定义 i = s; j = e; i,j 都为数组下标  *  *          从j向前搜索找第一个小于 X 的元素,  i , j 交换 --------------步骤1  *          从i向前搜索找第一个大于 X 的元素,  i , j 交换 --------------步骤2  * 重复步骤 1 2 直到 i = j *  * **** 总的思想就是以PivotKey为中心分解成两拨数的时候 一直都是PivotKey这个数一直在交换 所以我们需要交换一次 就得调转方向 * 以便让比较的范围逐渐缩小 也让PivotKey一直向中心靠近 */public static void Divide(int s, int e) {int i, j;i = s;j = e;boolean isLeft = true; // 表示当前PivotKey是在相对于左边一点的位置还是右边一点的位置       // 其实就是上一次交换中PivotKey是被换到左边还是右边if (i >= j) {     return;}while (i != j) {if (data[j] < data[i]) { //  j,i对应的数组元素必有一个是PivotKey  (慢慢根据结果分析得出)                     //  当 j = PivotKey,按步骤2                      //  当 i = PivotKey,按步骤1                      //  所以一个判断就搞定  是不是niceint temp = data[i];data[i] = data[j];data[j] = temp;isLeft = (isLeft == true) ? false : true;}if (!isLeft) {    // 每次比较之后,如果PivotKey在右边,根据步骤1,左边的指针后移,反之i++;} else {j--;}}// while循环结束 i,j 相等 停在PivotKey上 所以递归求解时 必须i,j分离i--;j++;
                //递归时刻  两次递归调用 分别对应分解出的左边和右边的数组Divide(s, i);  Divide(j, e);}/** *  * 输出当前数组 */public static void PrintData() {for (int i = 0; i < 10; i++) {System.out.print(" " + data[i]);}System.out.println("");}public static void main(String[] args) {data = new int[10];Scanner scanner = new Scanner(System.in);System.out.println("输入十个数字进行快速排序");for (int m = 0; m < 10; m++) {data[m] = scanner.nextInt();}Divide(0, 9);PrintData();scanner.close();}}
代码固定了只输入十个数    是为了方便观察结果  数组长度可以自定义,稍微修改下代码即可 


示例输入 : 41 1 23 14 78 55 69 15 6 51
每次交换后的数组变化 :
6 1 23 14 78 55 69 15 41 51
6 1 23 14 41 55 69 15 78 51
6 1 23 14 15 55 69 41 78 51
6 1 23 14 15 41 69 55 78 51
1 6 23 14 15 41 69 55 78 51
1 6 15 14 23 41 69 55 78 51
1 6 14 15 23 41 69 55 78 51
1 6 14 15 23 41 51 55 78 69
1 6 14 15 23 41 51 55 69 78

以上面数据为例   说一下第一次分解的过程

开始   : 41  1  23  14  78  55  69  15  6  51

以41 为PivotKey  , i = 0, j = 9  ,  data[j] < data[i]   即51<41 不满足 ; 

 j-- 变为8  , 6 <41 满足if判断,交换。  

数组变为 :6 1 23 14 78 55 69 15 41 51

 然后i++ 变为1  ,1 不满足 , i++ 变为2 ,23 不满足,i++ 变为3  ,14也不满足,i++变为4 ,78满足交换

数组变为:  6 1 23 14 41 55 69 15 78 51

此时j-- , 变为 7,15 < 41 ,满足 交换

数组变为:  6 1 23 14 15 55 69 41 78 51

此时i++,变为5 ,41 <55  满足交换

数组变为:  6 1 23 14 15 41 69 55 78 51

此时j-- ,变为6 ,69>41 不满足 ,j-- 变为5

 i == j  while循环结束 进入递归。。。。。。。


快速排序的平均时间复杂度 :O(n*lgn) 最坏时间复杂度为 : O(n^2),最好情况:O(nlgn)  空间复杂度 : O(lgn)  


 * 快速排序的时间复杂度与    划分是否对称   有关;
 *   ***最坏情况** 为:每次递归都划分为产生两个区域分别包含 n-1和 1 个元素  由于分解当前数组这部分的时间复杂度为O(n),
 *   所以快排的时间复杂度T(n)满足:
 *               { O(1)             n<=1
 *   T(n) =  |          
 *               { T(n-1) + O(n)    n>1
 *   解此递归方程得  T(n) = O(n^2)
 * 
 *   ***最好情况*** 为:每次划分的 PivotKey都处在最中间,即产生了两个大小为 n/2的区域
 *               { O(1)             n<=1
 *   T(n) =  |          
 *               { 2T(n/2) + O(n)   n>1
 *   解得 :  T(n) = O(nlgn)  


快速排序最坏的情况就是待排数据基本有序,退化为冒泡排序,时间复杂度为O(n^2)

快速排序递归过程对应一棵二叉树,其递归工作栈的大小与递归调用二叉树的深度相对应,最好情况下是一棵完全二叉树 


可以证明快速排序在平均情况下时间复杂度也是O(nlgn)   ~~~~~怎么证明的搞不懂

下一讲将最简单的交换类排序:冒泡










2 0
原创粉丝点击