快速排序 Java实现以及实际效率对比
一、快速排序的Java实现
二、快速排序的时间复杂度以及与归并排序的实际效率对比
快速排序是分治法的一种应用,主要思路是以一个基准值为参照,通过将小于基准值的数放到基准值的左边,将大于基准值的数放到右边,然后在对左右分别递归排序。
详细的思路以及图解可以参照其他资料,但网络上某些Java实现存在一定的问题,学习的时候跟着别人的代码思路走也很难受,这里主要提供一种快速排序的Java实现。
测试了很多次完全升序和降序,以及千万级别长度的数组,正确性应该没有问题,如有错误请指正。
package com.ryo.algorithm.sort; public class QuickSort implements Sort{ private int cursorLo; private int cursorHi; private int baseVal; private int temp; @Override public int[] sort(int[] arr) { sort(arr ,0 ,arr.length-1); return arr; } public void sort(int[] arr ,int low ,int high) { if(high - low > 1) {//进入此if说明子数组长度至少为3,可以进行排序 baseVal = arr[low];//保存基准值 cursorLo = low + 1;//左侧游标 cursorHi = high;//右侧游标 while(cursorLo != cursorHi) { while(cursorLo < cursorHi && arr[cursorLo] <= baseVal) { cursorLo++; } while(cursorHi > cursorLo && arr[cursorHi] >= baseVal) { cursorHi--; } swap(arr ,cursorLo ,cursorHi);//当cursorLo == cursorHi时只是原地交换 } if(arr[cursorLo] < baseVal) { swap(arr ,cursorLo ,low); cursorLo--;//为下一次递归准备边界值 } else { swap(arr ,cursorLo-1 ,low); cursorLo = cursorLo - 2;//为下一次递归准备边界值 } sort(arr ,low ,cursorLo); sort(arr ,cursorLo+2 ,high);//若cursorLo+2 > high或其他情况也没关系,开始执行后由于sort方法两个if的存在什么也不会发生 } else if(high - low == 1) {//需要快排的子数组长度仅为2的时候,只做比较交换的工作 if(arr[low] > arr[high]) { swap(arr ,low ,high); } } } private void swap(int[] arr ,int index1 ,int index2) { temp = arr[index1]; arr[index1] = arr[index2]; arr[index2] = temp; } }
二、快速排序的时间复杂度以及与归并排序的实际效率对比:
快速排序的时间复杂度:
Cbest(n) = nLog2(n)
Cworst(n) = n^2
Cavg(n) = 2nLn(n) ≈ 1.39nLog2(n)
与归并排序的效率对比
测试环境:Jre1.8_161 eclipse Version: Oxygen.3a Release (4.7.3a)
测试内容:对0-1000000随机整数组成的长度一百万的数组进行排序
快速排序:
归并排序:
其实在测试中发现快速排序的运行时间跨度很大,最大的一次达到了170+ms,被我剔除了,而归并排序基本稳定在140 - 150ms左右,这是因为归并排序最坏、平均、最好的时间复杂度均接近O(nlog(n))
两种算法在这个数量级的对比下似乎快速排序优势也没那么明显
当然这里受具体实现细节的影响,为了凸显出算法本身的结构优势,这里使用千万级数组(对应随机数也扩展到相应的范围)再进行测试:
由于将一千万个结果打印出来很耗时,这里注释掉了打印语句
长度一千万:
快速排序:
归并排序:
长度两千万:
快速排序:
归并排序:
扩到到千万的级别之后再也没有出现归并排序快于快速排序的情况了,且差距随着长度的增加理所当然地在增大。