算法导论第七章 -- 快速排序

来源:互联网 发布:stl源码剖析 知乎 编辑:程序博客网 时间:2024/05/21 00:17
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">1.快速排序水中直接在原来的数组上面进行排序的算法,不用另外开辟新的空间。任意选择一个元素,当作主元,把剩下的所有元素,比它大的放在右边,比它小的放在左边。</span>

2.最差时间复杂度是 O(n^2),造成最差时间复杂度的原因是:每一次的partition(划分子数组)都会造成不平衡的划分,都会划分成为n-1 个

元素的子数组和0个元素的子数组,则每一次的运行时间加起来就是n^2。

3.平均时间复杂度是O(n * logn),除开上面的那种不平衡的划分,只要划分的子数组中的元素比例为常数,无论这个比例是多少,

算法的运行时间总是O(n * logn);

下面我们可以得到代码:

quicksort.h

#ifndef QUICKSORT_H#define QUICKSORT_H#include <iostream>#include <vector>int getPartition(std::vector<int>&, int start, int end);void quickSort(std::vector<int>&, int start, int end);void swap(int& first,int& second);#endif

其中:

1.getPartition()函数接受三个参数,第一个是待排序的数组,第二个是数组的起始下标,第三个是数组的长度(并不是结束下标,按照编程的区间要求)

函数返回一个int值,或者vector<T>::size_type 值,即作为主元的元素(不采用随机化快排的话一般都是数组的第一个或者最后一个元素)经过一个

getPartition()之后,所固定的下标(对于这个元素来说,位置已经不变了)。

2.quickSort()函数也接受三个三个参数,意义和上面一样,这个是一个递归函数。

3.swap() 交换两个元素的位置。



quicksort.cpp

#include "quickSort.h"void swap(int& first,int& second){int current = first;first = second;second = current;}int getPartition(std::vector<int>& target, int start, int end){int count = start;                //这个整数是用来记录要发生交换的元素的下标,考虑子数组,所以起始值要为数组的第一个元素的下标if (start == end)                //如果数组的长度为1,那么就直接返回起始元素的下标值。结束函数{return start;}<span style="white-space:pre"></span>/*这个是关键的部分,在下面解释*/for (int i = start + 1; i < end; ++i) {if (target[start] >= target[i]){count += 1;swap(target[count],target[i]);}}swap(target[start],target[count]);return count;}/*这个就是起始函数,<span style="font-family: Arial, Helvetica, sans-serif;">得到中间下标后,</span><span style="font-family: Arial, Helvetica, sans-serif;">会递归调用,并且要进行数组的长度判断:如果数组的起始大于或者等于数组的结束,那么就直接终止这个函数,不进行递归*/</span>void quickSort(std::vector<int>& target, int start, int end){if(start < end){int middle = getPartition(target, start, end);quickSort(target, start, middle);quickSort(target, middle + 1, end);}}

其中,getPartition()这个函数是关键函数,想法是:在对于数组的一遍扫描中:以第一个元素为主元,count表示后面元素小于主元的个数,循环里面的 i 

表示现在与主元比较的元素的下标,如果主元大于等于当前元素,那么count值加1,并且交换下标为count 和 i 的两个元素(会出现自己交换自己的情况)

最后再把count下标的元素和 主元交换,然后返回主元所在的下标。


count其实可以从0开始,因为偷懒,我就把它写成了 count = start;正确的写法应该是:

int count = 0;/*在交换的时候*/swap(target[count + start],target[i]);


在c++中,我很少用到<=,>=,<,>这些符号,因为用多了迭代器,所以习惯于用 != 。但是处理这样的下标值,还是用前面4个比较方便,

因为考虑到区间问题:半开半闭区间的编程:[start,end),这个性质很好地处理了我们的习惯(从1 开始)和在机器中(从0开始)的现实的矛盾。

上面的循环和递归调用的函数所传入的值,都是遵循着这个原则,

最后是主函数:


#include "quickSort.h"int main(){int a[15] = {13,19,9,5,12,8,7,4,4,21,12,2,6,11,19};std::vector<int> test(a,a+15);quickSort(test,0,test.size());std::vector<int>::size_type size = test.size();for(std::vector<int>::size_type i = 0; i != size; ++i){std::cout << test[i] << " ";}std::cout << std::endl;return 0;}

简单调用quickSort这个函数即可得到排序的结果。



最后:关于快排的随机化,改进随机化:(假设元素是互异的)

1.随机化:我们不再以数组的第一个元素作为主元了,而是在剩下的元素里面随机地挑一个元素作为主元,并在开始排序之前把选中的主元

  放到数组的第一个元素的位置上,通过这个随机,我们可以使得算法对于所有的输入都能得到较好的期望性能。假如要加代码也只是两行:

  一行调用rand函数,另一行交换位置,其他一样。

2.改进随机化:三数取中划分:我们还是在剩下的元素中随机找数字,但是这次找三个数字,并且取他们之中的中位数来作为主元,这个方法只会改变

 O(n * logn)的常数项因子。












0 0
原创粉丝点击