剑指Offer:快速排序原理及其两种实现方式(Java)
算法描述
1、直接插入排序算法
原理
快速排序是对冒泡排序的改进,它使用分治法的思想,每次循环根据指定的基准数,将其他元素分别放置其左右(升序排序,大的放右小的放左),第二次循环,以基准数为中心,分为左右两部分,每部分再通过新的基准数排序
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
- 基本步骤
① 从数列中挑出一个元素,称为 “基准”(pivot);
② 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
③ 递归地(recursive)对小于基准值元素的子数列和大于基准值元素的子数列排序。
性能分析 - 时间复杂度分析
快速排序的一次划分算法从两头交替搜索,直到low和high重合,因此其时间复杂度是O(n);而整个快速排序算法的时间复杂度与划分的趟数有关。
*理想的情况是,每次划分所选择的中间数恰好将当前序列几乎等分,经过log2n趟划分,便可得到长度为1的子表。这样,整个算法的时间复杂度为O(nlog2n)。
*最坏的情况是,每次所选的中间数是当前序列中的最大或最小元素,这使得每次划分所得的子表中一个为空表,另一子表的长度为原表的长度-1。这样,长度为n的数据表的快速排序需要经过n趟划分,使得整个排序算法的时间复杂度为O(n2)。 - 空间复杂度分析
*从空间性能上看,尽管快速排序只需要一个元素的辅助空间,但快速排序需要一个栈空间来实现递归。最好的情况下,即快速排序的每一趟排序都将元素序列均匀地分割成长度相近的两个子表,所需栈的最大深度为log2(n+1);但最坏的情况下,栈的最大深度为n。这样,快速排序的空间复杂度为O(log2n))。
分区实现方法
方法1:先从后向前,再从前向后,交替比较
方法2:挖坑填数
代码展示
import java.util.Arrays;
/**
* 描述:
* ① 快速排序的实现方法1:一趟循环,从前往后与基准值比较,大于--,小于交换
* ② 快速排序的实现方法2:挖坑填数字,两头双向扫描
**/
public class 快速排序 {
public static void main(String[] args) {
int[] a = {12,20,5,16,15,1,30,45,23,9};
int start = 0;int end = a.length-1;
sort1(a,start,end);
for(int i = 0;i <a.length;i++){
System.out.println(a[i]);
}
int[] a2 = {8,2,9,1,5,10,12,3,18,6};
System.out.println(Arrays.asList(a2));
quickSort(a2,0,a.length-1);
for(int i = 0;i <a2.length;i++){
System.out.println(a2[i]);
}
// System.out.println(Arrays.asList(a2));
}
//实现方法1
public static void sort1(int[] a,int low,int high){
int start = low;
int end = high;
int key = a[low];
while(end > start){
//从后往前比较,没有遇到比关键值小的,end往前进
while(end > start && a[end] >= key)
end--;
//遇到比关键值小的,就和start位置进行交换
if(a[end] <= key){
int temp = a[end];
a[end] = a[start];
a[start] = temp;
}
//从前往后比较,没有遇到比关键值大的,start往后走
while(end > start && a[start] <= key)
start++;
//遇到比关键值大的,就和end位置进行交换
if(a[start] >= key){
int temp = a[start];
a[start] = a[end];
a[end] = temp;
}
//此时,第一次循环比较结束,关键值的位置已经确定了
//相当于一个分界线,左边的值都比关键值小,右边的值都比关键值大
}
//递归
//左边序列。第一个索引位置到关键值索引-1
if(start>low)
sort1(a,low,start-1);
//右边序列。从关键值索引+1到最后一个
if(end<high)
sort1(a,end+1,high);
}
//快速排序实现方法2:挖空填数
public static void quickSort(int[] a,int low,int high){
if(low<high){
int partition = partition(a,low,high);
quickSort(a,low,partition-1);
quickSort(a,partition+1,high);
}
}
public static int partition(int[] a,int low,int high){
int pivot = a[low];
int i = low,j = high;
while(j>i){
while(j>i && a[j]>=pivot)
j--;
a[i] = a[j];
while (i<j && a[i]<=pivot)
i++;
a[j] = a[i];
}
a[i] = pivot;
return i;
}
}