针对重复键值的3路快速排序算法
来源:互联网 发布:同济大学网络教育 编辑:程序博客网 时间:2024/06/03 23:49
针对待排序列中农存在大量重复键值的情况,上一节讲了一种对快排算法的优化,代码如下:
template<typename T>int __patition(T arr[], int l, int r){ swap(arr[l], arr[rand() % (r - l + 1) + l]); T v = arr[l]; int i = l + 1, j = r; while(true) { while(i <= r && arr[i] < v) { i++; } while(j >= l + 1 && arr[j] > v) { j--; } if(i > j) { break; } swap(arr[i++],arr[j--]); } swap(arr[j], arr[l]); return j;}template<typename T>void __quickSort(T arr[], int l, int r){ if(l >= r) { return; } else { int p = __patition(arr, l, r); __quickSort(arr, l, p - 1); __quickSort(arr, p + 1, r); }}template<typename T>void quickSort(T arr[], int n){ srand(time(0)); __quickSort(arr, 0, n - 1);}实际上还存在一种针对重复键值的经典优化快排算法,称为3路快速排序算法,这种排序算法相比上一节的优化效率更高,3路快排将待排序列分为3部分:<v、==v、>v,则进行递归时,对==v的部分就不用考虑,只需考虑对<v和>v部分进行递归排序,如图所示:
若 i 指向的元素<v,则交换arr[lt+1]和arr[i],然后i++,若 i 指向的元素==v,则直接i++,若 i 指向的元素>v,则交换arr[i]和arr[gt-1],此时不需要维护 i ,只需要将gt--即可,整个过程就是这样的,最后的结果如下图所示:
最后将arr[l]和arr[lt交换]。
接下来递归只针对<v和>v的部分进行递归即可,可以看出,对于含有很多重复键值的序列来说,可以省去对==v部分进行下一层递归操作,这将节省很多时间。
3路快速排序算法的C++实现如下,我们将之前讲的2种快排和3路快排进行时间性能上的比较:
SortTestHelper.h文件(包含辅助函数)
#include <iostream>#include <cstdlib>#include <ctime> //clock()、CLOCKS_PER_SEC#include <cassert> //包含函数assert()using namespace std;namespace SortTestHelper{ //辅助函数 - 随机产生一个数组 int* generateRandomArray(int n, int RangeL, int RangeR) //返回数组首地址 { //判断RangeL是否<=RangeR assert(RangeL <= RangeR); //参数为表达式,表达式为真时返回true,否则打印错误信息 int *arr = new int[n]; srand(time(0)); for(int i = 0; i < n ; i++) { arr[i] = rand() % (RangeR - RangeL + 1) + RangeL; //使得产生的随机数在RangeL和RangeR之间 } return arr; } //辅助函数 - 产生一个近乎有序的随机数组 int* generateNearlyOrderedArray(int n, int swapTime) { int *arr = new int[n]; for(int i = 0; i < n; i++) { arr[i] = i; //先生成一个完全有序的数组 } //然后交换几组元素,使之变成无序但近乎有序的数组 srand(time(0)); for(int j = 0; j < swapTime; j++) { //随机生成一个x位置和y位置 int posx = rand() % n; int posy = rand() % n; //交换x和y处的元素 swap(arr[posx], arr[posy]); } return arr; } //辅助数组 - 产生一个完全有序数组 int* generateTotallyOrderedArray(int n) { int *arr = new int[n]; for(int i = 0; i < n; i++) { arr[i] = i; } return arr; } //辅助函数 - 打印数组 template<typename T> void printArray(T arr[], int n) { for(int i = 0; i < n; i++) { cout << arr[i] << " "; } cout << endl; } //辅助函数 - 判断数组是否有序(升序) template<typename T> bool isSorted(T arr[], int n) { for(int i = 0; i < n - 1; i++) { if(arr[i] > arr[i + 1]) { return false; } } return true; } //辅助函数 - 测试算法的时间 template<typename T> void testSort(string sortname, void(*sort)(T[], int), T arr[], int n) //arr[]和n是函数指针需要的参数 { clock_t starttime = clock(); sort(arr, n); //调用函数sort() clock_t endtime = clock(); //判断排序是否成功 assert(isSorted(arr, n)); //若是数组无序,则assert会自动调用abort()退出程序,不会执行下面的语句 cout << sortname << " needs " << double(endtime - starttime) / CLOCKS_PER_SEC << "s." << endl; } //辅助函数 - 拷贝数组 int* copyIntArray(int a[], int n) { int *arr = new int[n]; //使用C++函数copy() copy(a, a + n, arr); return arr; }}
#include <iostream>#include <ctime>#include <cstdlib>#include "SortTestHelper.h"using namespace std;//对arr[l...r]进行插入排序template<typename T>void insertionSort(T arr[], int l, int r){ for(int i = l + 1; i <= r; i++) { T e = arr[i]; int j; for(j = i; j > l && arr[j - 1] > e; j--) { arr[j] = arr[j - 1]; } arr[j] = e; }}//3路快速排序template<typename T>void __quickSort3Ways(T arr[], int l, int r){ if(r - l <= 15) //序列中仅有16个元素时 { insertionSort(arr, l, r); return; } else { //因为需要返回lt和gt两个下标,所以这里不适用函数patition来进行划分 //patition操作 swap(arr[l], arr[rand() % (r - l + 1) + l]); T v = arr[l]; int lt = l; //保证<v的部分arr[l+1...lt]初始为空 int gt = r + 1; //保证>v的部分arr[gt...r]初始为空 int i = l + 1; //保证==v的部分arr[lt+1...i)初始为空 while(i < gt) { if(arr[i] < v) { swap(arr[lt + 1], arr[i]); i++; lt++; } else if(arr[i] == v) { i++; } else { swap(arr[i], arr[gt - 1]); gt--; } } //此时i == gt swap(arr[l], arr[lt]); //对arr[l...lt-1]进行递归 __quickSort3Ways(arr, l, lt - 1); //对arr[gt...r]进行递归 __quickSort3Ways(arr, gt, r); }}template<typename T>void quickSort3Ways(T arr[], int n){ srand(time(0)); __quickSort3Ways(arr, 0, n - 1);}//快排2template<typename T>int __patition2(T arr[], int l, int r){ swap(arr[l], arr[rand() % (r - l + 1) + l]); T v = arr[l]; int i = l + 1, j = r; while(true) { while(i <= r && arr[i] < v) { i++; } while(j >= l + 1 && arr[j] > v) { j--; } if(i > j) { break; } swap(arr[i++],arr[j--]); } swap(arr[j], arr[l]); return j;}template<typename T>void __quickSort2(T arr[], int l, int r){ //优化底层递归 if(r - l <= 15) //即只有16个元素时 { //采用插入排序 insertionSort(arr, l, r); return; } else { int p = __patition2(arr, l, r); __quickSort2(arr, l, p - 1); __quickSort2(arr, p + 1, r); }}template<typename T>void quickSort2(T arr[], int n){ srand(time(0)); __quickSort2(arr, 0, n - 1);}//快排template<typename T>int __patition(T arr[], int l, int r){ swap(arr[l], arr[rand() % (r - l + 1) + l]); T v = arr[l]; int j = l; for(int i = l + 1; i <= r; i++) { if(arr[i] < v) { swap(arr[j + 1], arr[i]); j++; } } swap(arr[l], arr[j]); return j;}template<typename T>void __quickSort(T arr[], int l, int r) //对arr[l...r]进行快速排序{ //优化底层递归 if(r - l <= 15) //即只有16个元素时 { //采用插入排序 insertionSort(arr, l, r); return; } else { int p = __patition(arr, l, r); //__patition()函数将arr[l...r]分为两部分,该函数返回一个索引值 __quickSort(arr, l, p - 1); __quickSort(arr, p + 1, r); }}template<typename T>void quickSort(T arr[], int n){ srand(time(0)); //设置时间种子 __quickSort(arr, 0, n - 1);}int main(){ int n = 700000; //测试 - 随机序列 int *arr = SortTestHelper::generateRandomArray(n, 0, n); int *arr2 = SortTestHelper::copyIntArray(arr, n); int *arr3 = SortTestHelper::copyIntArray(arr, n); SortTestHelper::testSort("quickSort", quickSort, arr, n); SortTestHelper::testSort("quickSort2", quickSort2, arr2, n); SortTestHelper::testSort("quickSort3Ways", quickSort3Ways, arr3, n); cout << endl; delete[] arr; delete[] arr2; delete[] arr3; //测试 - 近乎有序序列 int swaptime = 100; int *a = SortTestHelper::generateNearlyOrderedArray(n, swaptime); int *a2 = SortTestHelper::copyIntArray(a, n); int *a3 = SortTestHelper::copyIntArray(a, n); SortTestHelper::testSort("quickSort", quickSort, a, n); SortTestHelper::testSort("quickSort2", quickSort2, a2, n); SortTestHelper::testSort("quickSort3Ways", quickSort3Ways, a3, n); cout << endl; delete[] a; delete[] a2; delete[] a3; //测试 - 重复键值序列 int *b = SortTestHelper::generateRandomArray(n, 0, 10); int *b2 = SortTestHelper::copyIntArray(b, n); int *b3 = SortTestHelper::copyIntArray(b, n); SortTestHelper::testSort("quickSort", quickSort, b, n); SortTestHelper::testSort("quickSort2", quickSort2, b2, n); SortTestHelper::testSort("quickSort3Ways", quickSort3Ways, b3, n); cout << endl; delete[] b; delete[] b2; delete[] b3; return 0;}
测试结果如下:
可以看出,对于随机待排序列和近乎有序序列,3种快排算法的时间性能差不多,而且都符合理想性能;在含有大量重复键值的序列中,3路快速排序算法的时间性能会更好。
可以根据实际情况选取三种快排算法中的一种进行应用。
阅读全文
0 0
- 针对重复键值的3路快速排序算法
- 快排算法的针对重复键值的优化
- 微软面试题:三路划分快速排序---针对重复关键字的改进
- 快速排序(Java),针对重复元素
- 算法导论7-2,针对相同元素的快速排序
- 键值可以重复的排序链表
- 算法笔记 //05_有重复元素的排列问题(针对字母排序)
- 快速排序(2)算法改进--小的子文件、三者取中、重复关键字三路划分
- 针对相同元素值的快速排序
- 算法导论-7-2 针对有相同元素值的快速排序
- 算法导论第七章思考题7-2针对相同元素的快速排序-c++
- java 两个相同元素的List,合并排序去除重复元素的快速算法
- 排序算法3-快速排序
- 重复键值对的解决方案
- java map的重复键值的排序http://tbwuming.iteye.com/blog/1873634
- 排序算法之三路划分的快速排序
- 算法3:最常用的排序——快速排序
- 算法3:最常用的排序——快速排序
- 线程共享的环境包括:进程代码段、进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)、进程打开的文件描述符、信号的处理器、进程的当前目录和进程用户ID与进程组ID。 进程拥有这
- RecyclerView中EditText数据混乱,FoldingCell折叠布局第一次点击有问题
- CasperJS API 第一篇
- 10游标
- 线程环境下生成随机数之java.util.Random与java.util.concurrent.ThreadLocalRandom
- 针对重复键值的3路快速排序算法
- 修改mysql root账户密码
- ZooKeeper(一)-在Linux上的单机模式搭建
- Beanutils.copyproperties用法
- Java8新特性之四函数式接口
- gradle版本问题解决方案
- 一元运算符+
- 如何解决ajax跨域问题(转)
- SQL导入 timestamp 数据时报错