快速排序O(nlogn)

选择数组中的一个元素pivot,该元素作为基准

注意:将小于基准的元素移到左边,大于基准的元素移到右边(分区操作

数组被pivot分为两部分,继续对剩下的两部分做同样的处理

直到所有子集元素不再需要进行上述步骤

一.单向指针扫描

void swap(int* a, int* b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

//分治
int partition(int ar[], int p, int r) {
    
    int big = r, scan = p + 1;//左右指针
    int pivot = ar[p];//目标元

    //最终big下标<scan下标
    while (scan<=big) {
        //数组可能越界,于是下方加入big > scan
        if(scan<=big && ar[scan] > pivot) {
            swap(&ar[scan], &ar[big]);//如果scan扫描到比大于pivot,就scan就与big交换,最终big那部分元素一定要比scan部分大
            big--;
        }
        else {
            scan++;
        }
    }
    swap(&pivot, &ar[big]);//最后再将目标元与big指向的值交换
    
    return big;//返回big值
}

//快排进行
void quicksort(int ar[], int p, int r) {
    //当pivot>=区域右边界时终止
    if (p < r) {
        int q = partition(ar, p, r);//q=big
        //下方进行递归
        quicksort(ar, p, q - 1);//左部分
        quicksort(ar, q + 1, r);//右部分
    }
}

如上图,最终b下标小于a下标示数。

二.双向指针扫描

void swap(int* a, int* b)
{
    int tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}

//分治
int partition(int ar[], int p, int r) {
    //左右双指针
    int left = p + 1, right = r;
    int pivot = ar[p];
    
    //下标
    while (left <= right) {

        //left一直向右走,扫描到大于pivot停止
        while (left <= right&&ar[left] <= pivot) {
            left++;
        }
        //right一直向左走,扫描到小于pivot停止
        while (left <= right&&ar[right] >= right) {
            right--;
        }
        //上面2个while循环退出的时候,即遇到大于情况,进行交换
        if (left < right) {
            swap(&ar[left], &ar[right]);
        }
    }
    swap(&pivot, &ar[right]);//退出时,right指向最后一个小于主元的位置
    //这样,right位置就是左右两区域临界处
    return right;
}
    
    
//快排进行
void quicksort(int ar[], int p, int r) {
    //当pivot>=区域右边界时终止
    if (p < r) {

        int q = partition(ar, p, r);//q=right

        //下方进行递归
        quicksort(ar, p, q - 1);//左部分
        quicksort(ar, q + 1, r);//右部分

    }
}

如图,两种临界情况都是b下标<a下标,并且a位于最后一个<处

三.三指针分区

void swap(int* a, int* b)
{
    int tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}

//分治
int partition(int ar[], int p, int r) {
    //三指针
    int scan=p+1, equal=scan, big=r;
    int pivot = ar[p];

    //下标
    while (big > scan) {
        //scan扫描小于就向右移动
        while (ar[scan] <=pivot) {
            scan++; equal++;
            //当遇到相等时,将scan向右移一位,而equal还在scan的上一位,于是将equal值与scan调转
            if (ar[scan] = pivot) {
                scan++;
                swap(&ar[equal], &ar[scan]);
            }
        }
        //遇到大的时,将big与scan值互换,big向左移
        while (ar[scan] > pivot){ 
            swap(&ar[scan], &ar[big]);
            big--; 
        }
    }
    return big;
}
    
    

void quicksort(int ar[], int p, int r) {
    //当pivot>=区域右边界时终止
    if (p < r) {

        int q = partition(ar, p, r);//q=big

        //下方进行递归
        quicksort(ar, p, q - 1);//左部分
        quicksort(ar, q + 1, r);//右部分

    }
}

优化pivot的选择

A.三点中值

取p,r,mid三点中的中间值作为主元

int choose(int ar[],int p, int r) {
    int mid = p + (r - p) << 1;
    int pivot = -1;
    if (ar[p] > ar[r] && ar[p] < ar[mid]) {
        pivot = p;
    }
    else if (ar[r] > ar[p] && ar[r] < ar[mid]) {
        pivot = r;
    }
    else pivot = mid;
    return pivot;
}

B.绝对中值,O(n)

int chooose(int ar[],int r,int p) {
    int size = r + p - 1;//原数组大小
    int group = (size % 5==0) ? (size / 5) : (size / 5 + 1);//进行分组
    int* help, index = 0;
    //利用循环,对每一组进行插入排序,寻找中间值
    for (int i = 0; i < group; i++) {
        if (i = group - 1) {
            //先对最后一组进行插入排序;
            help[index++] = //最后一组排序后中间值;
        }
        else {
            //排序其他组;
            help[index++]= //排序后中间值;
        }
    }
    //对help数组进行插入排序,选取中间值
    return //中间值
}

C.调用插入排序

已知插入排序时间复杂度为O(n^2),在数值个数位于8之前,插入排序要比快排快

于是可以在8之前调用插入排序。

if (r - p + 1 <= 8) {
    //调用插入排序
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~失之东隅,收之桑榆~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值