思想
快速排序是基于分治策略的。对一个子数组A[p…r]快速排序的分治过程的三个步骤为:
分解:
数组A[p…r]被划分成两个(可能空)子数组A[p…q-1]和A[q+1…r],使得A[p…q-1]中的每个元素都小于等于A[q],且小于等于A[q+1…r]中的元素。下标q也在这个划分过程中进行计算。
解决:
通过递归调用快速排序,对子数组A[p…q-1]和A[q+1…r]排序。
合并:
因为两个子数组就是原地排序的,将它们的合并不需要操作:整个数组A[p…r]已排序。
伪代码
算法的关键是求q的partition过程,它实现子数组A[p,..,r]的原地址重排
伪代码
算法选取数组的右边界A[r]作为分裂元素
动画
动画来源
讲解
可以看出p-i部分的数都小于等于x,i+1–j部分的数都大于j
在(b)情况:i++,交换后A[i]的值小于等于x,而原始A[i]的值本身就是大于x的交换到j的位置当然也是大于x的
最后当j==r时候,i++后也需要进行交换,虽然A[i]大于x,当时A[r]的值是等于x。快排是:左边的是小于等于x,右边的大于x
partition函数
public int partition(int [] A,int low ,int high){
int x = A[high];
int i = low - 1;
for( int j = low;j<= high - 1;j++){
if( A[j] <= x){
i = i + 1;
swap(A,i,j);
}
}
i = i + 1;
swap(A,i,high);
return i;
}
public void swap(int[] array,int i,int j){
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
求划分id的partition函数的另外一种写法
核心思想:找到左侧大于x的数a,找到右侧小于x的数b,a、b交换,继续寻找,两个指针相遇时候结束,这样结束后左侧的数小于想,右侧的数大于x,两侧的数递归上面方法
public int Paratition(int[] A,int left ,int right){
if(left> right)
return -1;
int i = left;
int j = right;
int mid = A[left];
if(i<j){
while(i<j){
while(i<j && mid< A[j])
j--;
if(i<j){
A[i] = A[j];
i++;
}
while(i<j && A[i]< mid)
i++;
if(i<j){
A[j] = A[i];
j--;
}
}
A[i] = mid;
}
return i;
}
快排函数
public void quickSort(int[] A,int left,int right){
if(left>right)
return;
int id = partition(A,left,right);
quickSort(A,left,id-1);
quickSort(A,id+1,right);
}
复杂度分析
(1)时间复杂度
最好情况:
O(nlog2n)
最坏情况:原始数组有序
O(n2)
,当原始数组接近有序的时候算法效力低
平均时间复杂度
O(nlog2n)
(2)空间复杂度
快排是递归进行的,递归需要栈的辅助,空间复杂度
O(log2n)