快排基本思想:
采用二分的思想。每次从待排序的数组中选取一个元素作为基准,将所有小于基准的元素放到其左侧,大于基准的元素放到其右侧。这样整个数组成为一个局部有序的数组。然后再分别对左侧、右侧的数组进行快速排序,直到数组中元素个数小于等于1。
为什么快排的平均时间复杂度是O(nlogn),而冒泡、选择、插入排序都是O(n^2)?
对于冒泡/选择/插入:
每一趟排序,遍历n个元素,时间复杂度O(n),确认1个元素的位置。
确认n个元素的位置需要n趟排序。
对于快排(或者其他二分思想的排序)在平均情况下:
第1趟排序,遍历n个元素,时间复杂度O(n),确认1个元素的位置;
第2趟排序,遍历n个元素,时间复杂度O(n),确认2个元素的位置;
第3趟排序,遍历n个元素,时间复杂度O(n),确认4个元素的位置;
第4趟排序,遍历n个元素,时间复杂度O(n),确认8个元素的位置;
……
每次排序确认的元素个数呈2的指数倍增长,因此确认n个元素只需要logn趟排序。
这也是为什么快速排序最坏情况会达到O(n^2),因为二分失效,导致每次只能确认一个元素。当然也有一些方法用于避免这种最坏情况(下次再补这部分的内容吧)
具体实现:
每一趟选第一个元素作为基准。两个指针l和r分别指向开始元素和结尾元素。
void quickSort(vector<int>&heights,int start,int end){
if(end-start<=0) return;
//
int q=heights[start]; //基准元素
int i=start,j=end; //两个指针,i向右移动,j向左移动
while(i<j){
if(heights[i]>q){
swap(heights[i],heights[j]);
swap(names[i],names[j]);
j--;
}
else{
i++;
}
}
//判断指针所指的值是否大于基准元素,这里i不会越界
if(heights[i]>q)i--;
swap(heights[i],heights[start]);
//二分
quickSort(heights,start,i-1,names);
quickSort(heights,i+1,end,names);
}