快速排序
核心思想:
- 选区基准元素,将序列分为两部分,前半部分小于基准元素,后半部分大于基准元素。我们要做的就是左右扫描,当左边扫描到大于基准元素和右边扫描到小于基准元素 时,交换左右元素的位置,继续扫描交换,直到最后左右指针重合时,将基准元素插入中间位置,也就是将基准元素与重合指针指向的元素交换。然后将左右两部分分别进行快速排序。
具体操作:
- 选取一个基准元素(可随便选,为了编码方便,一般选取第一个元素或者最后一个元素),这里选取最后一个元素
- 从左到右一直找到大于基准元素的位置(若没有大于基准位置的元素,最终i指向的就为基准元素)停止(i为左指针)
- 再从右向左一直找到小于基准元素的位置(若没有小于基准位置的元素,最终j=i时停止)停止(j为右指针)
- 左边找到大于基准元素,右边找到小于基准元素后,交换他们的位置。(若左右指针相同,证明i,j指向位置的元素前面都小于基准元素,后面的元素(包括i,j指向的元素)大于等于基准元素,所以交换基准元素和i,j指向的元素)
- 此时已经将整个序列分为两部分,前半部分小于基准元素,后半部分大于基准元素,再分别进行快速排序即可
时间复杂度:
- O(nlog2^n)
- (因为快速排序,每次递归都将一个序列分为两份,1——>2——>4——>8.类似一个树结构,所以时间复杂度为O(nlog2^n))
适用场景:
- 快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
稳定性:
扩充理解:
- 根据基准元素的选择,扫描顺序也会有所变化,为了方便编码,一般选取第一个元素或者最后一个元素。
- 若基准元素选取最后一个元素:此时必须从左向右扫描,因为最后要把i,j指向位置的元素和基准元素做交换,所以i,j指向的元素必须要是大于等于基准元素的。若没有大于基准元素的元素,i最终会指向基准元素(即等于基准元素)。这样交换过去,后面部分的元素才会都大于等于基准元素。若从右向左扫描的话,最终i,j重合位置指向的元素是小于基准元素的(交换就会出现问题)
- 若基准元素选取第一个元素:此时必须从右向左扫描,因为最后要把i,j指向位置的元素和基准元素做交换,所以i,j指向的元素必须要是小于等于基准元素的。若没有小于基准元素的元素,i最终会指向基准元素(即等于基准元素)。这样交换过去,前面部分的元素才会都小于等于基准元素。若从左向右扫描的话,最终i,j重合位置指向的元素是大于基准元素的(交换就会出现问题)
java源码:
public class QuickSort {
public static void swap(int []a,int x,int y){
int temp=a[x];
a[x]=a[y];
a[y]=temp;
}
public static void quickSort(int[]a,int left,int right){
//基准元素选取最右边的元素
if(left>=right)return;//若一个元素或无元素则退出
//i,j为左右指针(使用i,j来作为左右指针,而不直接使用left,right作为指针是因为后面递归快速排序左右两部分的时候需要用到left,right)
int i=left;
int j=right;
while(true){
while(i<j&&a[i]<=a[right]){//从左到右一直找到大于基准元素的位置(若没有大于基准位置的元素,最终i指向的就为基准元素)停止
i++;
}
while(j>i&&a[j]>=a[right]){//从右向左一直找到小于基准元素的位置(若没有小于基准位置的元素,最终j=i)停止
j--;
}
if(i==j){//若左右指针相同,证明i,j指向位置的元素前面都小于基准元素,
swap(a,i,right);//所以交换基准元素和i,j指向的元素,此时已经将整个序列分为两部分,前半部分小于基准元素,后半部分大于基准元素
//对左右部分分别进行快速排序
quickSort(a,left,i-1);
quickSort(a,j+1,right);
return;
}else{//此时i指向的元素大于基准元素,j指向的元素小于基准元素,所以交换两个元素的位置,然后继续找,直到i==j
swap(a,i,j);
}
}
}
public static void main(String[] args) {
//测试
int []a={1,2,3,4,5,6};
quickSort(a,0,a.length-1);
for(int t:a)System.out.println(t);
}
}