改进的快速排序


/*
在北京工作的时候,接触到改进快速排序的需求.
在网上四处搜寻没有找到太合适的代码,可能是自己搜索的能力有待提升T_T
偶然间在新浪博客看到一篇改进快速排序的思路,苦于没有源码,所以自己按照作者的思路实现了一遍.由于时间过去太久,找不到作者的博客链接了,T_T
*/
//源码如下,仅供参考.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#ifdef WINVER
    #include <windows.h>
#else
    #include <sys/time.h>
#endif

void insert_sort(int a[], int low, int high);   //插入排序
void int_swap_(int *a, int *b);   //交换两个数
void compare_int_swap(int *a, int *b); // 交换后保证b是最大的
void array_pri(int a[], int low, int high);  //数组输出
void quick_sort(int a[], int low, int high); //快排
int tim_subtract(struct timeval *result, struct timeval *x, struct timeval *y);

int main()
{
    //1.在堆上开辟空间用来存储数组
    int low = 0;
    int high = 100000000 - 1;
    int *a = (int *)malloc(sizeof(int)*(high + 1));

    //2.初始化数组
    printf("正在初始化数据。。。\n");
    for (int i = low; i <= high; ++i)
    {
        a[i] = (int)(rand() % ((high + 1)*10));
    }

    //3.开始计时
#ifdef WINVER
    LARGE_INTEGER m_nFreq;
    LARGE_INTEGER m_nBeginTime;
    LARGE_INTEGER nEndTime;
    QueryPerformanceFrequency(&m_nFreq); // 获取时钟周期
    QueryPerformanceCounter(&m_nBeginTime); // 获取时钟计数
#else
    struct timeval  start,stop,diff;
    gettimeofday(&start,0);
#endif

    //4.执行快排序
    printf("开始排序。。。\n");
    quick_sort(a, low, high);

    //5.快排结束后的系统时间
#ifdef WINVER
    QueryPerformanceCounter(&nEndTime);
    printf("Total Use:%lfs\n", (double)(nEndTime.QuadPart-m_nBeginTime.QuadPart)*1000/m_nFreq.QuadPart)
#else
    gettimeofday(&stop,0);
    tim_subtract(&diff,&start,&stop);
    printf("Total Use:%d.%ds\n", (int)diff.tv_sec, (int)diff.tv_usec);
#endif

    //6.释放堆空间
    free(a);
    a = NULL;
    return 0;
}

void insert_sort(int a[], int low, int high)
{
    if (low >= high)
    {
        return;
    }
    //1.将最小的元素移动到最左边,下面的循环将会很好些
    int posMin = low;  //记录最小元素
    for (int i = low; i <= high; ++i)
    {
        if (a[i] < a[posMin])
        {
            posMin = i;
        }
    }
    int_swap_(&a[low], &a[posMin]);
    //2.如果小于三个数返回
    if (high - low < 2)
    {
        return;
    }
    //3.从第三个数开始插入
    for (int i = low + 2; i <= high; ++i)
    {
        int j = i;
        int t = a[i];
        while (a[j - 1]>t)
        {
            a[j] = a[j - 1];
            a[j - 1] = t;
            --j;
        }
    }
}

void quick_sort(int a[], int low, int high)
{
    if (low >= high)
    {
        return;
    }
    //1.数据较小的情况下,用插入法提高效率
    if (high - low < 20)
    {
        insert_sort(a, low, high);
        return;
    }
    //2.采用三值取中 选取哨兵    low (low+high)/2  high
    compare_int_swap(&a[low], &a[(low + high) / 2]);
    compare_int_swap(&a[low], &a[high]);
    compare_int_swap(&a[high], &a[(low + high) / 2]);     // a[low]<=a[high]    a[high]<=a[(low + high) / 2]
    //3.每次分化的时候分为 大于哨兵(stand)的  小于哨兵的  等于哨兵的   //p q i j

    //   - - - - - - - - - -
    //   5 5 2 3 1 8 9 5 5 5
    //     p     i j   q   stand
    int stand = a[high];
    int i = low - 1;
    int same_low = low - 1;
    int j = high;
    int same_high = high;  // a[high]=stand

    while(1)
    {
        //1.从左开始找第一个比stand大的位置 从右开始找第一个比stand小的位置
        while (a[++i] < stand)
        { }
        while (a[--j] > stand)
        {
            if(j == low)
            {
                break;
            }
        }
        if(i>=j)  //如果找不到两个位置
        {
            break;
        }
        //交换这两个位置
        int_swap_(&a[i], &a[j]);
        //如果这两个值有和stand相等的 左边加到最左边 右边加的最右边
        if (a[i] == stand)
        {
            int_swap_(&a[++same_low], &a[i]);
        }
        if (a[j] == stand)
        {
            int_swap_(&a[--same_high], &a[j]);
        }
    }
    //将stand放在合适的位置
    int_swap_(&a[i], &a[high]);
    //将左右两边的相等元素移动到中间来
    j = i - 1; //j是stand左边第一个位置
    i = i + 1; //i是stand右边第一个位置
    int k;
    for (k = low; k <= same_low&&j > same_low; ++k, --j)
    {
        int_swap_(&a[k], &a[j]);
    }
    j = j - same_low + k - 1;

    for (k = high - 1; k >= same_high&&i < same_high; --k, ++i)
    {
        int_swap_(&a[k], &a[i]);
    }
    i = i + k - same_high + 1;

    quick_sort(a, low, j);
    quick_sort(a, i, high);
}

void int_swap_(int *a, int *b)
{
    *a = *a ^ *b;
    *b = *a ^ *b;
    *a = *a ^ *b;
}

void compare_int_swap(int *a, int *b)
{
    if (*a > *b)
    {
        int_swap_(a, b);
    }
}

void array_pri(int a[], int low, int high)
{
    for (int i = low; i <= high; ++i)
    {
        printf("%d ", a[i]);
    }
    printf("\n");
}

int tim_subtract(struct timeval *result, struct timeval *x, struct timeval *y)
{
    //形参安全检查
    if(x->tv_sec > y->tv_sec)
    {
        return -1;
    }
    result->tv_sec = (y->tv_sec - x->tv_sec);
    result->tv_usec = (y->tv_usec - x->tv_usec);
    if (result->tv_usec<0)
    {
        result->tv_sec--;
        result->tv_usec+=1000000;
    }
    return 0;
}

/*
 * time : 2017年2月16日21:19:24
 * loca : BeiJing,China
 * auth : GuoRui
 * email: corcpp@163.com corcpp@qq.com
 *
 */

测试了下,内排序1亿个数,耗时在18秒内. 等有时间在研究下继续改进…..

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值