快速排序算法与插入排序算法的结合

在这一篇中笔者要讲的是插入排序算法与快速排序算法的结合,为什么要这样结合使用?因为插入排序对基本排好序的数组来做排序的速度很快,而快速排序能将无序数组快速的变化为基本有序,那大家可能就问,就使用快排就行了嘛,的确,使用快排也是很快速的,但是在接近排序完成的时刻,换成插入排序算法更加能提高排序速度,笔者写两个程序比较下就知道了。

首先第一个程序是单纯的使用快排:

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));
	}
	
}
这里和上面同样,笔者也是使用了十万个数据进行验证,也是方便看出差距,下面是运行结果:

第一个算法:


第二个算法:


差距已经出来了,而且随着数据的增加,差距也会更大,所以相比之下,第二个算法的时间复杂度会比较低,相对应的代价就是程序的复杂度也会上升。








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值