- 快速排序是对冒泡排序的改进。
-
- 快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序,它采用一种分治(Divide-and-ConquerMethod)的方法
- 快速排序的思想:
-
- 在数组中找到一个基准数(pivot)
- 分区,将数组中比基准数大的放到它的右边,比基准数小的放到它的左边
- 继续对左右区间重复第二步,直到各个区间只有一个数,这时候,数组也就有序了。
- 代码:
-
1 int Partition(vector<int> &v, int head, int rear){ 2 int key = v[head]; 3 while (head < rear){ 4 while (v[rear] <= key && head < rear){ 5 --rear; 6 } 7 swap(v[head], v[rear]); 8 while (v[head] >= key && head < rear){ 9 ++head; 10 } 11 swap(v[rear], v[head]); 12 13 } 14 v[head] = key; 15 ++my_count; 16 return head; 17 } 18 19 void QuickSort(vector<int> &v, int head, int rear){ 20 int pivot = -1; 21 if (head < rear){ 22 pivot = Partition(v, head, rear); 23 QuickSort(v, head, pivot - 1); 24 QuickSort(v, pivot + 1, rear); 25 } 26 }
- Note: Partition函数中 v[rear] <= key 以及 v[head] >= key 表达式必须包含等于的判断,否则当数组两头的数相等时将会造成死循环 例如 {5,2,6,2,9,10,5}
- Partition函数:最慢情况下快速排序会进行 size()- 1 次 Partition函数,而每次调用,Partition函数会选择一个基准数,例如v[head]或者v[rear],或者任意一个数组中的数。之后分别从两头扫描,碰到比基准数大或者小的数就与上一个head或rear交换,或者直到head大于等于rear时,此次循环结束。
- QuickSort函数:该函数采用递归的方法,每次调用一次Partition函数,得到一个基准数的索引和相对基准数有序的数列,之后将该基准数左边的数组和右边的数组分别调用QuickSort函数,也就是它本身。直到数组中只有一个数时,这条递归序列便结束。
- 基于快速排序的查找前k个最大数
-
- 由上可知,快排的思想是每次找到一个基准数,将数组排列成基准数左边的每个数都比基准数大,右边的每个数都比基准数小的序列。
- 通过这个思想,我们可以稍微修改QuickSort函数,使它变成QuickSearch函数,使之拥有快速查找前k个最大的数。
-
1 int QuickSearch(vector<int> &v, int head, int rear, int k){ 2 int pivot = -1, len = 0; 3 if (head < rear){ 4 pivot = Partition(v, head, rear); 5 len = pivot - head + 1; 6 if (len < k){ 7 pivot = QuickSearch(v, pivot + 1, rear, k - len); 8 } 9 else if (len > k){ 10 pivot = QuickSearch(v, head, pivot - 1, k); 11 } 12 } 13 return pivot; 14 }
- 上图中,我们可以发现,函数参数多了一个k,这个值是表示要获取前k个最大数。
- 函数中多了一些逻辑,每次执行完Partition函数,根据获取的基准值索引,计算基准值左边数组的长度。
- 若len < k,则说明,在基准值左边的数组中已经有了len个最大数,此时,我们只需在基准值右边的数组再找k - len个最大数即可,所以只要再次调用QuickSearch函数,并传入k-len参数以及基准值右边的数组索引。
- 若len > k,则说明,此时基准值左边已经有了len个最大值,然而len大于k,我们并不需要那么多的最大值,所以再次调用QuickSearch函数,传入基准值左边的数组索引,以及k,获得这个长度len的最大数集的子集