算法学习 - 快速排序相关

来源:互联网 发布:js插件和组件的区别 编辑:程序博客网 时间:2024/05/21 11:41


1. 划分

快速排序中最重要的步骤就是划分,即选择一个元素(很多地方称为枢纽元素),将序列划分成两部分,比枢纽元素大的放在枢纽元素的右边,比枢纽元素小的放在枢纽元素的左边。

划分函数的思想:

1. 选择一个枢纽元素(以选择序列的第一个元素为例),暂存到临时变量中

2. 从序列的右边向左查找,找到一个比枢纽元素小的值。若找到,则将这个值填入到枢纽元素的位置

3. 从序列的左边向右查找,找到一个比枢纽元素大的值。若找到,则将这个值填入到step 2中那个比枢纽元素小的元素的位置。

4. 重复step2, step3,直到向右查找的“指针”(索引)和向左查找的“指针”(索引)碰到一起。表明一次划分就完成了。

按照上述思想实现代码如下:

int partition(int *arr, int l, int r){int i = l, j = r;int key = arr[l];//枢纽元素while (i < j){while (i < j && arr[j] >= key) j--;//从右往左找到一个比枢纽小的if (i < j){arr[i] = arr[j];i++;}while (i < j && arr[i] < key) i++;//从右往左找到一个比枢纽大的if (i < j){arr[j] = arr[i];j--;}}arr[i] = key;return i;}


2. 递归快排

递归的方式是最常见的方式,因为递归的写法代码简短。

算法思想:对元素进行划分,划分一次之后分别对两个子序列进行划分。直到子序列的元素个数变成1为止。

void QuickSort(int *arr, int l, int r){if (l < r){int par = partition(arr, l, r);QuickSort(arr, l, par - 1);QuickSort(arr, par + 1, r);}}


3. 使用栈作为辅助空间的快排

递归的过程其实就可以理解为一个栈,因为递归是依靠函数调用压栈的方式实现的。所以我们可以手动将中间过程保留,来进行快排。

算法思想:

1. 对序列进行一次划分,划分后的两个子序列头尾都压入栈中。

2. 在栈非空的情况下,从栈中取出两个元素(子序列的头尾位置)。对子序列进行划分。

3. 同样,对子序列进行划分的时候也将划分后的序列头尾压入栈中。

4. 重复step2 step3,知道栈为空为止。

void QuickSort2(int *arr, int l, int r){stack<int> tmp;//临时栈if (l < r)//{int par = partition(arr, l, r);//if (l < par - 1)//{tmp.push(l);//tmp.push(par - 1);}if (par + 1 < r){tmp.push(par + 1);tmp.push(r);}while (!tmp.empty()){r = tmp.top(); tmp.pop();l = tmp.top(); tmp.pop();par = partition(arr, l, r);if (l < par - 1){tmp.push(l); tmp.push(par - 1);}if (par + 1 < r){tmp.push(par + 1); tmp.push(r);}}}}


4. 使用队列作为辅助空间的快排

这个实现其实和用栈作为辅助空间的思想是相同的,无非就是将存储中间结果的辅助空间换成了队列。

void QuickSort3(int *arr, int l, int r){queue<int> tmp;//临时队列if (l < r){int mid = partition(arr, l, r);//一次划分if (l < mid - 1)//划分中间结果入队列{tmp.push(l);tmp.push(mid - 1);}if (mid + 1 < r){tmp.push(mid + 1);tmp.push(r);}while (!tmp.empty()){l = tmp.front(); tmp.pop();r = tmp.front();tmp.pop();mid = partition(arr, l, r);if (l < mid - 1){tmp.push(l); tmp.push(mid - 1);}if (mid + 1 < r){tmp.push(mid + 1);tmp.push(r);}}}}


5. 快排什么时候适合使用,什么时候不适合使用?

快排是基于划分的,最好的情况就是每次划分恰好都是在序列的中间,这样算法的复杂度就是O(n*lgn)。所以说枢纽元素的选择会直接影像到排序的效率。

随机序列快排:枢纽元素的选择也将是随机的,这时候的划分不会导致出现最差的结果。

已序序列快排:假设采用枢纽元素总是选择第一个元素的方式,很明显,每次划分都只能排序一个元素,这时候排序的效率就是最差的(也就是严重的不平衡树)。此时效率为O(n^2)。

那么对于这种已序的序列什么排序比较适合呢?插入排序!!对于插入排序,若序列是已序的表明序列不需要作很多的移动,此时效率为O(n)。


这也就可以联系到STL中sort函数的实现:

STL中的sort(),在数据量大时,采用quicksort,分段递归排序;一旦分段后的数量小于某个门限值,改用Insertion sort,避免quicksort深度递归带来的过大的额外负担,如果递归层次过深,还会改用heapsort。


1 0
原创粉丝点击