02——归并排序、快速排序(O(nlogn))

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zxt_1/article/details/85019105

1.mergeSort

在这里插入图片描述

gf
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

优化

在这里插入图片描述
在这里插入图片描述

应用:逆序对

// 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);
}

猜你喜欢

转载自blog.csdn.net/zxt_1/article/details/85019105