经典排序算法——快速排序及其优化

快速排序

快速排序的思想:通过一趟排序将数据分割成独立的两部分,其中一部分数据都比另一部分的所有数据都要小,然后再按照此方法对这两部分数据分别进行快速排序,可以递归的进行。

时间复杂度:

  • 好的情况(无序的):O(nlog2n)
  • 坏的情况(有序的):O(n^2) 
快速排序法不稳定。

让我们先通过一个例子来了解一下:

12    5    4   25    10    15

首先:我们选择 12 为基准,比 12 小的放左边。

5     4    10    12    25    15

同样,用快速排序对 12 的前半部分进行排序:

4    5    10    12    25    15

此时,前半部分排序完毕,后半部分一样用快速排序:

4    5    10    12    15    25

我们可以用递归的方法来求解。

下面我们换个例子来深入的理解快速排序算法。

23  4  34  54  65  76  22  3  56  13  35  28  32  43  5

如下图所示,为第一趟的快速排序。

此时,23 的前半部分都比 23 小,23 的后半部分都比 23 大。

我们可以对它的前半部分和后半部分分别进行快速排序,直到排序结束。

下面我们用 java 来实现快速排序算法。

public static void quickSort(int[] array){
	Quick(array,0,array.length -1);
}

public static void Quick(int[] array,int start,int end){
	int par = partion(array,start,end);
	if(par > start + 1){
		Quick(array,start,par-1);//对前半段进行快速排序
	}
	if(par < end-1){
		Quick(array,par+1,end);	//对后半段进行快速排序。
	}
}

下面这个函数是返回基准位置的。即上面的例子中第一趟快速排序后 23 的位置。 

public static int partion(int[] array,int start,int end){
	int tmp = array[start];
	while(start < end){
		while(start < end && array[end] >= tmp){
			--end;
		}
		if(start >= end){
			break;
		}else{
			array[start] = array[end];
		}
		while(start < end && array[start] <= tmp){
			++start;
		}
		if(start >= end){
			break;
		}else{
			array[end] = array[start];
		}
	}
	array[start] = tmp;
	return start;
}

这是我们用递归来解决它,其实还有另外一种办法,我们可以利用栈来解决这个问题。

还是上面的例子,让我们来分析一下。

23  4  34  54  65  76  22  3  56  13  35  28  32  43  5

首先:我们来给来个快速排序,将它分成两部分。

5  4  13  23  22  23  76  65  56  54  35   28  32  43  34

然后:和前面一样,我们分别对左边和右边进行快速排序,但不同的是,这次我们要把下标 start 和 end 存入栈中,然后将右边的下标 start 和 end 也存入栈中。

如图


接着我们从栈里取出,使 low 和 high 分别指向 start 和 end ,然后进行快速排序。


依次类推,我们一边向栈里取出元素,一边向栈里存储元素,直到栈为空,表示我们的排序结束。

下面我们用 java 实现一下这个非递归的。

public static void QuickSort(int[] array){
	int[] stack = new int[array.length];
	int top = 0;
	int low = 0;
	int high = array.length -1;
	int par = partion(array,low,high);
	//入栈
	if(par > low+1){
		stack[top++] = low;
		stack[top++] = par-1;
	}
	if(par < high-1){
		stack[top++] = par+1;
		stack[top++] = high;
	}
	//出栈
	while(top > 0){
		high = stack[--top];
		low = stack[--top];
		par = partion(array,low,high);
		if(par > low+1){
			stack[top++] = low;
			stack[top++] = par-1;
		}
		if(par < high-1){
			stack[top++] = par+1;
			stack[top++] = high;
		}
	}	

快速排序的优化

快速排序有以下优化方案

  1. 当待排序数组当中数据比较少的时候,用直接插入法。
  2. 聚集相同元素法。
  3. 随机选取基准法。


当数据比较少时,采用直接插入法排序

当数据比较少时,采用直接插入法排序比较高效。

所以我们只需要在快速排序的方法上增加一个判断语句即可

以下为用 java 实现

public static void Quick1(int[] array,int start,int end){
		//当数据少于 100 时用直接插入法排序即可
		if((end - start + 1) < 100){
			insertSort(array,start,end);
			return;
		}
		int par = partion(array,start,end);
		if(par > start + 1){
			Quick1(array,start,par-1);
		}
		if(par < end-1){
			Quick1(array,par+1,end);
		}
	}


聚集相同元素法

简单来说就是找和基准相同的元素,这样子可以减少递归的次数。

下面是把和基准相同的元素聚集到一起。


这里附上代码

    public static int[] focus(int[] array,int start,int end,int par,int left,int right){
		//聚集和基准相同的元素
		//左边找
		int parleft = par-1;
		int parright = par + 1;
		for(int i = par-1;i >= start;i--){
			if(array[i] == array[par]){
				if(i != parleft){
					swap(array,i,parleft);
					parleft--;
				}else{
					parleft--;
				}
			}
		}
		left = parleft;
		//右边找
		for(int j = par+1;j <= end;j++){
			if(array[j] == array[par]){
				if(j != parright){
					swap(array,j,parright);
					parright++;
				}else{
					parright++;
				}
			}
		}
		right = parright;
		int[] brray = new int[2];
		brray[0] =left;
		brray[1] = right;
		return brray;
	}

然后我们对于基准左边和基准右边分别进行排序,和优化前不同的是我们不用再比较和基准相同的元素了。

对于左边,我们令 low = 0,high = pl-1。

对于右边,我们令 low = pr-1, high = 长度-1。

public static void Quick2(int[] array,int start,int end){
		int par = partion(array,start,end);
		int  left = par-1;
		int right = par+1;
		int[] b = focus(array,start,end,par,left,right);
		left = brrar[0];
		right = brrar[1];
		if(par > start + 1){
			Quick2(array,start,left);
		}
		if(par < end-1){
			Quick2(array,right,end);
		}
	}


随机选取基准法。

在数据已经有效的情况下,快速排序时间复杂度太高,为了解决这个问题,课一随机选取基准来提高效率。

public static void Quick3(int[] array,int start,int end){
		int par = partion(array,start,end);
		//假设已经有序,就会提高效率
		swap(array, start, (int)(Math.random()*10%(end-start))+start);
		if(par > start + 1){
			Quick(array,start,par-1);
		}
		if(par < end-1){
			Quick(array,par+1,end);
		}
	}

猜你喜欢

转载自blog.csdn.net/alyson_jm/article/details/80304210
今日推荐