Java 快速排序
2016-02-28 13:59
483 查看
快速排序作为一种快速的排序算法,所以现在就详细的介绍一下 。
快速排序是冒泡的一种改进 都是交换类的排序算法
( 冒泡排序是每轮都把当前排序范围中最大的数放到最后 )
快速排序是确定一个PivotKey ,把当前数组范围分成三份,左边是小于PivotKey的,中间是PivotKey,右边是大于PivotKey的
当然可能PivotKey最小 没有左边的部分 ,反之。
快速排序是基于分治策略的一种算法 (分治法的思想是将一个规模为n 的问题分解成k个较小规模的子问题,子问题互相独立且与原问题相同)
快速排序的基本思想是三个步奏:
1,分解 : 通常以待排列数组第一个元素为 PivotKet(或用随机选择策略 随机选取其中一个元素作为PivotKey), 将数组划分成三段 , 中间是小于PivotKey的元素
中间是PivotKey 右侧是大于它的元素 (要是PivotKey为最小的元素 就直接去第二步)
2,递归求解:通过递归手段 对上一步的左边 和 右边的数组进行快速排序
3,合并 ;由于对于子数组的排列是直接用的原数组,所以在递归结束,整个数组已经是有序的了 不需要额外的合并操作
上代码,这里的代码只是一种解法 别的解法暂无研究 ,代码有错误 或者不完美的留言指正,你说我就改,就怕你不来
示例输入 : 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) ~~~~~怎么证明的搞不懂
下一讲将最简单的交换类排序:冒泡
快速排序是冒泡的一种改进 都是交换类的排序算法
( 冒泡排序是每轮都把当前排序范围中最大的数放到最后 )
快速排序是确定一个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) ~~~~~怎么证明的搞不懂
下一讲将最简单的交换类排序:冒泡
相关文章推荐
- java工具类之导出Excel
- Java 代理
- spring 配置和实例
- java 知识体系
- Android Studio初体验之导入eclipse项目
- Eclipse is running in a JRE, but a JDK is required 解决方法
- 关于用struts2框架中iframe对应的jsp页面的不到action的值的问题
- 从Eclipse迁移到Android Studio
- JAVA通过ftp远程获取文件并压缩
- Java中的this与super
- ubuntu中eclipse无法识别android手机问题
- Java 问卷调查
- java 面向对象 OO 反射 "六原则一法则"
- 教大家看官方技术文档(一)- Spring MVC
- JAVA笔记整理(1):进制转换
- Ubuntu系统下运行Eclipse出现找不到jre的问题的解决方法
- 分享Ubuntu 14.04环境下如何下载安装android studio和jdk
- java的package和import机制
- Spring源码学习
- struts2整合spring