学习笔记----快速排序的java实现及其改良
2016-05-24 18:21
741 查看
快速排序算法是我们学习数据结构必学的算法之一,虽然在java中,对列表的排序可直接使用Collections.sort(List l) (这使用的是归并排序算法) ,对基本类型的数组也有Arrays.sort()(这使用的是改进的快速排序算法),但实现一个快速排序,仍能给自己很多启发。
很多算法书上都使用c或者c++实现快排算法,但是作为一个写惯了java的程序员,再去写c或c++有着各种水土不服(没有丰富的API库帮忙),加上jvm虚拟机性能的提高,也就可以用java回顾一下以前的快速排序算法,也算为面试做个准备。 快速排序的平均时间复杂度能达到O(n*log n),最坏情况下 (元素基本有序,中轴选择总是最大或最小 、元素,此时快速排序退化成冒泡排序)为O(n^2)。
本例中使用随机选择中轴元素的方式降低了最坏情况可能性,并在子数组比较小(在书上推荐是5-15个元素即比较小)的时候停止快排,采用插入排序(最好情况既元素基本有序下,时间复杂度为O(n))完成整个List的排序。 有关快速排序的原理,算法书上已经讲的很清楚了,按照我个人的理解,快速排序就是如何将整个数组通过选择的中轴(key)划分成两个子数组,并且满足(key)这种关系。这就算完成一趟快排了,剩下的只需要递归(key)即可。 快排代码如下:
然而比较下原快速排序和随机化快排的代码执行速度发现两者差别并不大,当然在电脑上还存在其它进程影响执行,不够准确,所以这个时间值就不贴出来了。
在JDK中,对快速排序做了如下三个优化,有兴趣的同学可以去看看Arrays.sort方法的源码。
1、就像上面提到的,对于元素数小于7(java内定值)的数组,直接返回插入排序的结果(因为插入排序对基本有序数组和简单数组能保证线性性能,所以一些O(n^2)的算法不一定就不能使用)
2、使用三平均划分法(数组最左边,最右边,最中间的元素的中位数)找到中轴。
3、对于这一点我还不太理解,即是说:将和中轴相等的元素都放到数组中间,如:4,3,1,15,15,15,23,34,21 . 这样只需排序(4,3,1)和(23,34,21)这三个子数组,对于重复元素多的数组可减少问题规模。
很多算法书上都使用c或者c++实现快排算法,但是作为一个写惯了java的程序员,再去写c或c++有着各种水土不服(没有丰富的API库帮忙),加上jvm虚拟机性能的提高,也就可以用java回顾一下以前的快速排序算法,也算为面试做个准备。 快速排序的平均时间复杂度能达到O(n*log n),最坏情况下 (元素基本有序,中轴选择总是最大或最小 、元素,此时快速排序退化成冒泡排序)为O(n^2)。
本例中使用随机选择中轴元素的方式降低了最坏情况可能性,并在子数组比较小(在书上推荐是5-15个元素即比较小)的时候停止快排,采用插入排序(最好情况既元素基本有序下,时间复杂度为O(n))完成整个List的排序。 有关快速排序的原理,算法书上已经讲的很清楚了,按照我个人的理解,快速排序就是如何将整个数组通过选择的中轴(key)划分成两个子数组,并且满足(key)这种关系。这就算完成一趟快排了,剩下的只需要递归(key)即可。 快排代码如下:
public class QuickSort { static List<Integer> list = new ArrayList<>(); private static int END_QUICKSORT = 7; public static void main(String[] args) { // TODO Auto-generated method stub for (int i = 0; i < 100000; i++) { list.add((int) (Math.random() * 1000)); //list.add(i); } long a1 = System.nanoTime(); quickSort(list); long a2 = System.nanoTime(); System.out.println(a2 - a1); /*for (Integer temp : list) { System.out.println(temp); }*/ } public static void quickSort(List<Integer> list) { quick(list, 0, list.size() - 1); // 对快排后基本有序数组进行一次插入排序 insertSort(list); } private static void insertSort(List<Integer> list2) { // TODO Auto-generated method stub for (int i = 1; i < list2.size(); i++) { int v = list2.get(i); int j = i - 1; for (; j >= 0 && list2.get(j) > v; j--) { list2.set(j + 1, list.get(j)); } list.set(j + 1, v); } } private static void quick(List<Integer> list, int low, int hight) { // TODO Auto-generated method stub if (low < hight) { int key = partition(list, low, hight); // 选择中轴并对子数组部分排序 if (key - low > END_QUICKSORT) quick(list, low, key - 1); // 对左半部分继续使用快排 if (hight - key > END_QUICKSORT) quick(list, key + 1, hight); // 当(key,hight)范围小到一定程度,不再进行快排 } } private static int partition(List<Integer> list, int low, int hight) { // TODO Auto-generated method stub int rand = (int) (Math.random() * (hight - low)) + low; int key = list.get(rand); if (rand != low) { int temp = list.get(low); list.set(low, key); list.set(rand, temp); } while (low < hight) { while (low < hight && list.get(hight) >= key) hight--; list.set(low, list.get(hight)); while (low < hight && list.get(low) <= key) low++; list.set(hight, list.get(low)); } list.set(low, key); return low; } }
然而比较下原快速排序和随机化快排的代码执行速度发现两者差别并不大,当然在电脑上还存在其它进程影响执行,不够准确,所以这个时间值就不贴出来了。
在JDK中,对快速排序做了如下三个优化,有兴趣的同学可以去看看Arrays.sort方法的源码。
1、就像上面提到的,对于元素数小于7(java内定值)的数组,直接返回插入排序的结果(因为插入排序对基本有序数组和简单数组能保证线性性能,所以一些O(n^2)的算法不一定就不能使用)
2、使用三平均划分法(数组最左边,最右边,最中间的元素的中位数)找到中轴。
3、对于这一点我还不太理解,即是说:将和中轴相等的元素都放到数组中间,如:4,3,1,15,15,15,23,34,21 . 这样只需排序(4,3,1)和(23,34,21)这三个子数组,对于重复元素多的数组可减少问题规模。
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序