在这一篇中笔者要讲的是插入排序算法与快速排序算法的结合,为什么要这样结合使用?因为插入排序对基本排好序的数组来做排序的速度很快,而快速排序能将无序数组快速的变化为基本有序,那大家可能就问,就使用快排就行了嘛,的确,使用快排也是很快速的,但是在接近排序完成的时刻,换成插入排序算法更加能提高排序速度,笔者写两个程序比较下就知道了。
首先第一个程序是单纯的使用快排:
public class MyArray {
private int nElem;
private long[] theArray;
public MyArray(int size){
theArray = new long[size];
nElem = 0;
}
public void insert(long data){
theArray[nElem] = data;
nElem++;
}
public void displayArray(){
System.out.println(" A = ");
for (int i = 0; i < nElem; i++) {
System.out.print(theArray[i] + " ");
}
System.out.println("");
}
这个是进行数组的创建,没什么大问题,然后是递归方法实现快速排序:
public void sort(){
quickSort(0, nElem-1);
}
public void quickSort(int letf, int right){ //递归调用快速排序
if (right - letf <= 0) {
return;
}
else {
long mediaNum = theArray[right];
int patation = patationIn(letf,right,mediaNum); //调用方法返回枢纽
quickSort(letf, patation-1);
quickSort(patation+1, right);
}
}
private int patationIn(int letf, int right, long mediaNum) { //循环划分大小,并且同时将枢纽的位置返回
int letfPtr = letf-1;
int rightPtr = right;
while(true){
while (theArray[++letfPtr] < mediaNum) {
;
}
while (rightPtr > 0 && theArray[--rightPtr] > mediaNum) {
;
}
if (letfPtr > rightPtr) {
break;
}else {
swap(letfPtr,rightPtr);
}
}
swap(letfPtr,right);
return letfPtr;
}
private void swap(int letfPtr, int rightPtr) {
long temp = theArray[rightPtr];
theArray[rightPtr] = theArray[letfPtr];
theArray[letfPtr] = temp;
}
这个是纯快速排序算法,使用了递归方法实现,从逻辑上比较容易理解,这里需要注意的是关于枢纽的选择,我选择的是数组最右边的元素作为枢纽,这样做的好处在于不用在下面的while循环中去增加对左引用,也就是letfPtr"指针"的条件判断,还有最后patation方法的最后一步是将枢纽放在右边数组端的最左边位置,并且返回枢纽的位置。
好了,我们来调用实现一下,由于要比较两种实现方法的时间开销的差异,所以笔者在这里使用了1000000也就好是十万个数组项,这样更好的看出差距。
以下是实现类:
public class TestQuickSort {
public static void main(String[] args) {
MyArray myArray = new MyArray(1000000);
for (int i = 0; i < 1000000; i++) {
int a = (int) (java.lang.Math.random()*99);
myArray.insert(a);
}
myArray.displayArray();
long time1 = System.currentTimeMillis();
myArray.sort();
long time2 = System.currentTimeMillis();
myArray.displayArray();
System.out.println("the time is : " + (time2 - time1));
}
}
运行结果等下再看,我们先看看第二个算法,关于将快速排序算法与插入算法的结合:
public class MyArray {
private long[] theArray;
private int nElem;
public MyArray(int size){
theArray = new long[size];
nElem = 0;
}
public void insert(long data){
theArray[nElem] = data;
nElem++;
}
public void display(){
System.out.println("A = :");
for (int i = 0; i < nElem; i++) {
System.out.print(theArray[i] + " ");
}
System.out.println("");
}
首先,也是对数组的创建,这个步骤和上个算法一样,就不再赘述,直接看排序算法:
public void quickSort(){
recQuickSort(0,nElem-1);
}
private void recQuickSort(int letf, int right) { //快速算法,递归调用自身
int size = right-letf+1;
if (size < 10) {
insertSort(letf,right);
}
else {
long mediaNumber = getMediaNumber(letf,right);
int patation = patationIn(letf,right,mediaNumber);
recQuickSort(letf, patation - 1);
recQuickSort(patation + 1, right);
}
}
private int patationIn(int letf, int right, long mediaNumber) { //划分算法
int letfPtr = letf;
int rightPtr = right - 1;
while(true){
while (theArray[++letfPtr] < mediaNumber) {
;
}
while (theArray[--rightPtr] > mediaNumber) {
;
}
if(rightPtr < letfPtr){
break;
}else {
swap(letfPtr,rightPtr);
}
}
swap(letfPtr,right-1); //放置枢纽的位置
return letfPtr;
}
private long getMediaNumber(int letf, int right) { //使用三数选一的方法选择枢纽
int center = (letf + right)/2;
if (theArray[letf] > theArray[center]) {
swap(letf,center);
}
if (theArray[letf] > theArray[right]) {
swap(letf,right);
}
if (theArray[center] > theArray[right]) {
swap(center,right);
}
swap(center,right-1);
return theArray[right-1];
}
private void swap(int letf, int right) { //两数交换
long temp = theArray[right];
theArray[right] = theArray[letf];
theArray[letf] = temp;
}
这里笔者选择当左右指针相差小于十的时候,调用插入排序算法进行排序,这里需要注意的是,在选择枢纽算法中,最后要将枢纽放在右边倒数第二位,这样放的原理是既不会妨碍之前就排序好的三个元素,也能像前面选择最右边为枢纽的算法一样进行接下来的排序。
好,然后是插入排序算法的实现:
private void insertSort(int letf,int right) { //插入排序算法
int in,out;
for (out = letf+1; out <= right; out++) {
long temp = theArray[out];
in = out;
while (in > letf && theArray[in-1] > temp) {
theArray[in] = theArray[in-1];
--in;
}
theArray[in] = temp;
}
}
这个没什么好说的,需要注意的是两个循环的嵌套时,要注意循环的条件,注意临界条件该怎么设置比较合理。
然后是这个算法的实现类:
public class QuickSortTestClass {
public static void main(String[] args) {
MyArray myArray = new MyArray(1000000);
for (int i = 0; i < 1000000; i++) {
int a = (int) (java.lang.Math.random()*99);
myArray.insert(a);
}
myArray.display();
long time1 = System.currentTimeMillis();
myArray.quickSort();
long time2 = System.currentTimeMillis();
myArray.display();
System.out.println("the time is : " + (time2 - time1));
}
}
这里和上面同样,笔者也是使用了十万个数据进行验证,也是方便看出差距,下面是运行结果:
第一个算法:
第二个算法:
差距已经出来了,而且随着数据的增加,差距也会更大,所以相比之下,第二个算法的时间复杂度会比较低,相对应的代价就是程序的复杂度也会上升。