- 快速排序
- 三向切分的快速排序
快速排序
讲解的十分生动形象
维护一个指针lo,维护一个指针hi,初始时lo指向数组的第一个元素,hi指向数组的最后一个元素。
对于快速排序来说其核心有如下几点
1. 选取一个元素(一般为数组的第一个元素)作为基准,可以生动的想象在基准元素上挖了一个坑,把基准元素挖了出来,并移植给了临时变量x
2. 既然将基准元素挖坑并取走那还需要把这个坑给填上,所以就从数组尾端向前遍历,直到遇到第i个位置的元素小于基准元素x,这时候就可以把第i个位置的元素挖走,并放到之前被挖走元素的位置上。(挖走只是形象的说法,其实该位置元素并没有物理上的变化,只是覆盖的过程)
3. 当经过第2步后第一步被挖走的位置已经被第二步挖来的元素填充,所以可以继续向后移动lo指针,直到遇到一个j位置使得j位置的元素大于x元素,将j位置挖走,放到第二步中空缺的位置处。
4. 直到lo和hi重合完成一次遍历,将x元素值填到此时的lo指针指向的位置,因为如果把上述步骤想象成挖坑填坑的话,当lo与hi重合的位置一定是最后一个需要填坑的位置,并且lo之前的值都小于等于x,之后的值都大于等于x
5. 递归执行。
public void sort(int []nums,int lo,int high){
if(lo>=high){//如果递归到子数组不存在元素时返回
return;
}
int x=nums[lo];
int begin=lo;
int end=high;
while(lo<high){
while(lo<high && nums[high]>=x){
high--;
}
if(lo<high){
nums[lo++]=nums[high];
}
while(lo<high && nums[lo]<=x){
lo++;
}
if(lo<high){
nums[high--]=nums[lo];
}
}
nums[lo]=x;
sort(nums,begin,lo-1);
sort(nums,lo+1,end);
}
三向切分的快排
快速排序的改进一般基于如下两点:
1. 当数组的规模较小时,快速排序的效率不如插入排序,因此当递归到数组规模较小时可以进行插入排序
修改代码为:
将:
if(lo>=high){
return;
}
修改为:
if(hi<=lo+M){
Insertion.sort(a,lo,high);
return;
}
2.对于存在大量重复元素的时候,可以采用三向切分的方式
其主要过程如下:
从左到右遍历一次,维护一个指针lt,使得a[lo,lt-1]的元素都小于v,
维护一个指针gt,使得a[gt+1,hi]的元素都大于v,
维护一个指针i,使得a[lt,i-1]的元素都等于v,
a[i,gt]的元素为还未参加排序的元素
如果a[i] < v 将a[i]与a[lt]元素进行交换,
并将lt和i都加一
如果a[i]>v 将a[gt]与a[i]进行交换,并将gt减一,因为换过了的元素不一定比v大所以不能将i加一
如果a[i]=v,则将i++
代码如下:
public void sort(int []a,int lo,int hi){
if(lo>=hi){
return;
}
int lt=lo;
int i=lo+1;
int gt=hi;
int x=a[lo];
while(i<=gt){
if(a[i]<x){
int tmp=a[lt];
a[lt]=a[i];
a[i]=tmp;
lt++;
i++;
}
if(a[i]>x){
int tmp=a[gt];
a[gt]=a[i];
a[i]=tmp;
gt--;
}
if(a[i]==x){
i++;
}
}
sort(a,lo,lt-1);
sort(a,gt+1,lt);
}