前言
你说你熟悉常用的数据结构和算法,谈谈快速排序算法吧。
解答
首先先给出快速排序的基本思想:
(1)先从数列中选择一个数作为基准数
(2)将序列分成两个区间,小于步骤(1)基准数的放在左区间,大于基准数的放在右区间。
(3)对步骤(2)分出的左右区间重复第二步,直到各区间只有一个数。(递归)
代码如下:
class QuickSort {
public:
int* quickSort(int* A, int n) {
// write code here
if(A==NULL||n<2) return A;
quickSortCore(A,0,n-1);
return A;
}
int RandomInRange(int min, int max)//随机产生min~max之间的一个数
{
int random = rand() % (max - min + 1) + min;
return random;
}
void quickSortCore(int *A,int left,int right)
{
if(left<right){
int random=RandomInRange(left,right);
swap(A,random,right);
int mid=partition(A,left,right);
quickSortCore(A,left,mid-1);
quickSortCore(A,mid+1,right);
}
}
int partition(int *A,int left,int right){
int pivot=left-1;
int index=left;
while(index<=right){
if(A[index]<=A[right]){
swap(A,++pivot,index);
}
++index;
}
return pivot;
}
void swap(int *arr, int index1, int index2) {
int tmp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = tmp;
}
};
快速排序的平均时间复杂度为O(N*logN),最坏的情况复杂度为O(N*N),最坏情况出现在每次切分选的切分元素总是当前切分数组的最小值时。因其在排序过程中,会交换元素打乱数组原本的相对顺序,所以快速排序是不稳定的算法。
快速排序改进:
- 因为在对于小数组来说,快速排序是比插入排序慢的,如果我们需要将我们的排序方法做成库函数的话,则我们面对的排序对象的特性是未知的,那么我们可以先判断数组的大小,如果其大小<某个值的时候,就用插入排序,大于某个值就用快速排序。
在实际应用当中,数组中可能存在着大量重复元素,如我们可能要排序一个公司的所有人员的年龄。在这种情况下,快速排序的递归性会使元素全部重复的子数组经常出现,我们可以将数组切分成三部分,分别为小于,大于和等于切分元素的数组元素。思想为从左遍历数组依次,维护一个lt指针使得a[lo….lt-1]的元素都小于切分元素v,一个指针gt使得A[gt+1…hi]中的元素都大于v,一个指针i使得A[lt…i-1]中的元素都等于v。遍历过程遵循的规则如下:
- A[i]小于v,将A[lt]和A[i]交换,++lt,++i
- A[i]大于v,将A[gt]和A[i]交换,gt–;
- A[i]等于v,++i;
代码如下:
void quickSortCore3(int *A,int lo,int hi)
{
if(hi<=lo) return;
int lt=lo,i=lo+1,gt=hi;
int v=A[lo];
while(i<=gt){
if(A[i]>v){
swap(A,i,gt--);
}else if(A[i]<v){
swap(A,lt++,i++);
}else{
++i;
}
}
quickSortCore3(A,lo,lt-1);
quickSortCore3(A,gt+1,hi);
}