单路
1 # include <iostream> 2 # include <ctime> 3 # include <algorithm> 4 # include "InsertionSort.h" 5 6 //对arr[l...r]部分进行partition操作 7 // 返回p,使arr[l...p-1] < arr[p]; arr[p+1...r] > arr[p] 8 template <typename T> 9 int __partition(T arr[], int l, int r){ 10 // 随机选用一个元素作为标定(避免有序时退化为O(n^2)) 11 swap( arr[l] , arr[rand()%(r-l+1)+l] ); 12 T v = arr[l]; 13 //arr[l+1...j] < v ; arr[j+1...i) > v 14 int j = l; 15 // 对整个数组遍历 16 for( int i = l + 1; i <= r; i++ ){ 17 //右侧 > v, 左侧 < v 18 if(arr[i] < v){ 19 swap( arr[j+1], arr[i] ); 20 j ++; 21 } 22 } 23 // 把v放到中间 24 swap( arr[l] , arr[j]); 25 return j; 26 } 27 28 template <typename T> 29 void __quickSort(T arr[], int l ,int r){ 30 if( l >= r) 31 return; 32 33 int p = __partition(arr, l, r); 34 __quickSort(arr, l, p-1); 35 __quickSort(arr, p+1, r); 36 } 37 38 template <typename T> 39 void quickSort(T arr[], int n){ 40 srand(time(NULL)); 41 __quickSort(arr, 0, n-1); 42 }
双路
1 template <typename T> 2 int __partition2(T arr[], int l, int r){ 3 swap( arr[l] , arr[rand()%(r-l+1)+l] ); 4 T v = arr[l]; 5 // 对整个数组扫描,保持如下性质 6 // arr[l+1...i) <= v; arr(j...r] >= v 7 int i = l+1,j = r; 8 while(true){ 9 while( i <= r && arr[i] < v ) i ++; 10 while( j >= l + 1 && arr[j] > v ) j --; 11 if( i > j ) break; 12 swap( arr[i] , arr[j] ); 13 i ++; 14 j --; 15 } 16 swap( arr[l] , arr[j] ); 17 return j; 18 } 19 20 template <typename T> 21 void __quickSort2(T arr[], int l ,int r){ 22 if( r - l <= 15 ){ 23 insertionSort(arr,l,r); 24 return; 25 } 26 27 int p = __partition2(arr, l, r); 28 __quickSort2(arr, l, p-1); 29 __quickSort2(arr, p+1, r); 30 } 31 32 template <typename T> 33 void quickSort2(T arr[], int n){ 34 srand(time(NULL)); 35 __quickSort2(arr, 0, n-1); 36 }
三路
1 // 三路快速排序处理 arr[l...r] 2 // 将arr[l...r]分为 <v ; ==v ; >v 三部分 3 // 之后递归对 <v ; >v 两部分继续进行三路快速排序 4 template <typename T> 5 void __quickSort3(T arr[], int l, int r){ 6 if(r - l <= 15){ 7 insertionSort(arr,l,r); 8 return; 9 } 10 11 swap(arr[l], arr[rand()%(r-l+1)+l]); 12 13 T v = arr[l]; // 取最左侧数据为标定 14 15 int lt = l; // arr[l+1...lt] < v 16 int gt = r + 1; // arr[gt...r] > v 17 int i = l + 1; // arr[lt+1...i) == v 18 while( i < gt ){ 19 if(arr[i] < v ){ 20 swap( arr[i], arr[lt+1]); 21 i ++; //i指向已处理元素,需处理 22 lt ++; 23 } 24 else if( arr[i] > v){ 25 swap( arr[i], arr[gt-1]); 26 gt --; // i指向未处理元素,不用处理 27 } 28 else{ // arr[i] == v 29 i ++; 30 } 31 } 32 33 swap( arr[l] , arr[lt] ); 34 35 __quickSort3(arr, l, lt-1); 36 __quickSort3(arr, gt, r); 37 38 } 39 40 template <typename T> 41 void quickSort3(T arr[], int n){ 42 srand(time(NULL)); 43 __quickSort3(arr, 0, n-1); 44 }
在O(n)内查找第k大元素
总结:
1、归并和快排都使用了分治算法
2、二者相比,归并的难点在于“合”,快排的难点在于“分”
3、树的实现也使用了分治思想