快速排序的优化

注:以下代码参考博客参考博客地址

快速排序的期望时间复杂度为O(nlogn),最坏时间复杂度为O(n²)。为了避免出现最坏情况,我们可以对其进行改进。

1.哨兵的改进,为了使每次划分的数不致于使两边相差过大,我们可以选择三者取中法选哨兵,即首尾和中间数的中位数来作为哨兵。
2.小数据量的改进,递归的快速排序在大概n<10的时候比插入排序要慢,所以我们在n<10的时候采用插入排序。
3.相同数字的改进,在存在大量相同数字的时候快速排序是很慢的,我们采用两个指针保存相同数字的信息,在划分时不用把它们算进去,这样就节省了大量的时间。
4.递归的优化。快排有两次递归调用,我们可以用循环代替后面的一次递归。
下面是具体的c代码
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#define Num 200000
void swap(int *a, int *b) 
{
    int t = *a;
    *a = *b;
    *b = t;
}


//函数作用:取待排序序列中low、mid、high三个位置上数据,选取他们中间的那个数据作为哨兵  
int SelectPivotMedianOfThree(int arr[],int low,int high)  
{  
     int mid = low + ((high - low) >> 1);//计算数组中间的元素的下标  
     
     //使用三数取中法选择哨兵  
     if (arr[mid] > arr[high])//目标: arr[mid] <= arr[high]  
     {  
          swap(arr+mid, arr+high);  
     }  
     if (arr[low] > arr[high])//目标: arr[low] <= arr[high]  
     {  
          swap(arr+low, arr+high);  
     }  
     if (arr[mid] > arr[low]) //目标: arr[low] >= arr[mid]  
     {  
          swap(arr+mid,arr+low);  
     }  
     //此时,arr[mid] <= arr[low] <= arr[high]  
     return arr[low];  
     //low的位置上保存这三个位置中间的值  
     //分割时可以直接使用low位置的元素作为哨兵,而不用改变分割函数了  
}  
//小数据的插入排序
void insert_sort(int a[], int low, int high)
{
    int i, j;
    for (i=low+1; i<=high; i++)
    {
        int key = a[i];
        j = i-1;
        while (j>=0 && key<a[j])
        {
            a[j+1] = a[j];
            j--;
        }
        a[j+1] = key;
    }
}
//快速排序,把哨兵左边和哨兵相同的元素移到最左边,右边和哨兵相同的元素移到最后边,在划分完之后又移到哨兵左右
void quick_sort(int arr[],int low,int high)  
{  
     int first = low;  
     int last = high;  
     
     int left = low;  
     int right = high;  
     
     int leftLen = 0;  
     int rightLen = 0;  
     
     if (high - low + 1 < 10)  
     {  
          insert_sort(arr,low,high);  
          return;  
     }  
     
     //一次分割  
     int key = SelectPivotMedianOfThree(arr,low,high);//使用三数取中法选择枢轴  
     
     while(low < high)  
     {  
          while(high > low && arr[high] >= key)  
          {  
               if (arr[high] == key)//处理相等元素  
               {  
                    swap(arr+right,arr+high);  
                    right--;  
                    rightLen++;  
               }  
               high--;  
          }  
          arr[low] = arr[high];  
          while(high > low && arr[low] <= key)  
          {  
               if (arr[low] == key)  
               {  
                    swap(arr+left, arr+low);  
                    left++;  
                    leftLen++;  
               }  
               low++;  
          }  
          arr[high] = arr[low];  
     }  
     arr[low] = key;  
     
     //一次快排结束  
     //把与哨兵key相同的元素移到哨兵最终位置周围  
     int i = low - 1;  
     int j = first;  
     while(j < left && arr[i] != key)  
     {  
          swap(arr+i,arr+j);  
          i--;  
          j++;  
     }  
     i = low + 1;  
     j = last;  
     while(j > right && arr[i] != key)  
     {  
          swap(arr+i, arr+j);  
          i++;  
          j--;  
     }  
     quick_sort(arr,first,low - 1 - leftLen);  
     quick_sort(arr,low + 1 + rightLen,last);  
}  

int main()
{
    int a[Num];
    int i;
    for (i=0; i<Num; i++)
    {
          a[i] = rand()%Num;//随机数来测试     
          //a[i] = 0;  //用相同的值来测试,结果vc直接结束了
     }
     
    //for (i=0; i<Num; i++)
     //   printf("%d ", a[i]);
     //printf("\nAferSort:\n");
     
    int a1=clock();    //获取快排执行的时间
    quick_sort(a, 0, Num-1);
  
     int b=clock();//到这结束
     int c=b-a1;//算出来的单位是毫秒
     printf("%d\n", c);
   // for (i=0; i<Num; i++)
   //     printf("%d ", a[i]);
     getchar();
    return 0;
}
递归的优化代码(经测试在数据量不是特别大的时候对效率的优化不大,所以列出来作为学习使用)
    void quick_sort(int arr[],int low,int high)  
    {   
        int pivotPos = -1;  
        if (high - low + 1 < 10)  
        {  
            InsertSort(arr,low,high);  
            return;  
        }  
        while(low < high)  
        {  
            pivotPos = Partition(arr,low,high);  
            QSort(arr,low,pivot-1);  
            low = pivot + 1;  
        }  
    }  
经测试,如果有大量数据相同的话,原来的快排是很慢的,而改良版的就很快了。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值