文章目录
1.mergeSort
优化
应用:逆序对
// merge函数求出在arr[l...mid]和arr[mid+1...r]有序的基础上, arr[l...r]的逆序数对个数
long long __merge( int arr[], int l, int mid, int r){
int *aux = new int[r-l+1];
for( int i = l ; i <= r ; i ++ )
aux[i-l] = arr[i];
// 初始化逆序数对个数 res = 0
long long res = 0;
// 初始化,i指向左半部分的起始索引位置l;j指向右半部分起始索引位置mid+1
int j = l, k = mid + 1;
for( int i = l ; i <= r ; i ++ ){
if( j > mid ){ // 如果左半部分元素已经全部处理完毕
arr[i] = aux[k-l];
k ++;
}
else if( k > r ){ // 如果右半部分元素已经全部处理完毕
arr[i] = aux[j-l];
j ++;
}
else if( aux[j-l] <= aux[k-l] ){ // 左半部分所指元素 <= 右半部分所指元素
arr[i] = aux[j-l];
j ++;
}
else{ // 右半部分所指元素 < 左半部分所指元素
arr[i] = aux[k-l];
k ++;
// 此时, 因为右半部分k所指的元素小
// 这个元素和左半部分的所有未处理的元素都构成了逆序数对
// 左半部分此时未处理的元素个数为 mid - j + 1
res += (long long)(mid - j + 1);
}
}
delete[] aux;
return res;
}
// 求arr[l..r]范围的逆序数对个数
long long __inversionCount(int arr[], int l, int r){
if( l >= r )
return 0;
int mid = l + (r-l)/2;
// 求出 arr[l...mid] 范围的逆序数
long long res1 = __inversionCount( arr, l, mid);
// 求出 arr[mid+1...r] 范围的逆序数
long long res2 = __inversionCount( arr, mid+1, r);
return res1 + res2 + __merge( arr, l, mid, r);
}
// 递归求arr的逆序数对个数
long long inversionCount(int arr[], int n){
return __inversionCount(arr, 0, n-1);
}
2.quickSort
2.1 一次partition操作
2.2 partition操作方式1
实现方式
//arr[l...p-1] <= arr[p] <= arr[p+1...r]
template <typename T>
int partition(T arr[], int l, int r)
{
int j = l;
int i = l+1;
while(i <= r)
{
if(arr[i] < arr[l])
{
swap(arr[j+1], arr[i]);
j++;
}
i++;
}
swap(arr[l], arr[j]);
return j;
}
//对arr[l...r]排序
template <typename T>
void __quickSort(T arr[], int l, int r)
{
if(l >= r) return ;
int p = partition(arr, l, r);
__quickSort(arr, l, p-1);
__quickSort(arr, p+1, r);
}
template <typename T>
void quickSort(T arr[], int n)
{
__quickSort(arr, 0, n-1);
}
优化
2.3 存在问题(数组近乎有序、数组存在大量重复元素)
快排退化为O(n^2)的原因是因为Partition时没有很好均分数组
2.3.1 数组近乎有序
解决方法
2.3.2 数组存在大量重复元素
解决方法1
解决方法2
template <typename T>
void __quickSort3Ways(T arr[], int l, int r){
// 对于小规模数组, 使用插入排序进行优化
if( r - l <= 15 ){
insertionSort(arr,l,r);
return;
}
// 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot
swap( arr[l], arr[rand()%(r-l+1)+l ] );
T v = arr[l];
int lt = l; // arr[l+1...lt] < v
int gt = r + 1; // arr[gt...r] > v
int i = l+1; // arr[lt+1...i) == v
while( i < gt ){
if( arr[i] < v ){
swap( arr[i], arr[lt+1]);
i ++;
lt ++;
}
else if( arr[i] > v ){
swap( arr[i], arr[gt-1]);
gt --;
}
else{ // arr[i] == v
i ++;
}
}
swap( arr[l] , arr[lt] );
__quickSort3Ways(arr, l, lt-1);
__quickSort3Ways(arr, gt, r);
}
template <typename T>
void quickSort3Ways(T arr[], int n){
srand(time(NULL));
__quickSort3Ways( arr, 0, n-1);
}