您的位置:首页 > 编程语言 > Java开发

Java 快速排序

2016-02-28 13:59 483 查看
快速排序作为一种快速的排序算法,所以现在就详细的介绍一下 。

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

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

快速排序是确定一个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
//  所以一个判断就搞定  是不是nice
int 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) ~~~~~怎么证明的搞不懂

下一讲将最简单的交换类排序:冒泡
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: