AcWing785 快速排序

目录

题目

代码

代码核心思路:

时间复杂度:O(nlongn)

解决以下问题


题目

代码

#include <iostream>

using namespace std;

const int N = 100010;
int a[N], n;

void quick_sort(int a[],int l,int r)
{
    if(l>=r)return ;
    int i=l-1,j=r+1;
    int x=a[(i+j)/2];
    
    while(i<j)
    {
        do(i++);while(a[i]<x);
        do(j--);while(a[j]>x);
        if(i<j)swap(a[i],a[j]);
    }
    
    quick_sort(a,l,j),quick_sort(a,j+1,r);
}

int main()
{
    cin >> n;
    for(int i = 0; i < n; i ++)cin>>a[i];
    
    quick_sort(a,0,n-1);
    
    for(int i=0;i<n;i++)cout<<a[i]<<' ';
    return 0;
}

代码核心思路:

在整个数组中,我们选取数组的中间一个数 x ,然后用两个指针 i j 从左右两边来遍历数组,当i \geq j时,停止遍历,我们需要把所有小于或等于x的数放到左边,把所有大于或等于x的数放到右边(注意:有可能左右两边同时存在等于x的数),这样子我们就把数组分成了两部分,继续递归处理左右两边的数组,当递归到子数组里面只存在一个数的时候,即 i \geq j 的时候return。在处理每一个子数组的时候,我们都会分裂出两个全新的子数组,在这两个全新的子数组里面a[l...i]\leqx,而且a[j...r]\geqx。采用分治的思想

时间复杂度:O(nlongn)

解决以下问题

问题1:

在快排函数quick_sort()函数的while循环语句里面,为什么要用do..while循环来写

while(i<j)
    {
        do(i++);while(a[i]<x);
        do(j--);while(a[j]>x);
        if(i<j)swap(a[i],a[j]);
    }

为什么我们不采用下面的代码呢?

while(i<j)
    {
        while(a[i]<x)i++;
        while(a[j]>x)j--;
        if(i<j)swap(a[i],a[j]);
    {

如果我们使用while语句:如果出现a[i]=x, a[j]=x的情况,那

while(a[i]<x)i++;
while(a[j]>x)j--;

就无法使i往右边走或者使j往左边走,会让程序陷入死循环中

所有我们使用do...while语句来迫使i往右走和j往左走,这也就解释了为什么一开始要把i的值设置成l-1,r的值设置成r+1。每一次递归之后,a[l...i]\leqx,a[j...r]\geqx

 问题2:

递归处理两个子数组的时候用到的为什么是j而不是i?

quick_sort(a,l,j),quick_sort(a,j+1,r);

首先我们要知道 j 取值范围是[l...r-1]

首先在while循环的do(j--);while(a[j]>x);里面就决定了 j 一定小于或等于r;当子数组里面就两个数的时候 j 有可能等于 l

那为什么j的最大值为r-1呢?

首先如果子数组里面只有一个数,那直接return掉,我们只考虑子数组里面的数大于1个数的时候。因为我们取的x是a[(i+j)/2],这意味着x是不可能为a[r]的,虽然他们的值有可能相等,但是x不会取到a[r]本身,这也就决定了do(j--);while(a[j]>x);这条语句至少执行两次!

同理 i 的取值范围为[l...r](这里的x取的是a[(i+j)/2])

所以在递归过程中不能出现i+1或者i-1,这也就解释了为什么这里使用j来递归

sj

在这里是满足a[l...j]\leqx,a[j+1...r]\geqx的

分析

分析1

if(i<j)swap(a[i],a[j])的<能能否换成\leq

可以,当i和j相等的时候换了和没换是一样de

分析2

什么时候才能使用i作为quick_sort的分界点?

当x取a[(l+r+1)/2]的时候,可以

原因:此时,i的取值范围是[l+1...r],而j的取值范围是[l...r],所以递归过程中不能出现j-1或者j+1的情况。

分析3

do i++; while(a[i] < x)do j--; while(a[j] > x) 中不能用a[i] <= x a[j] >= x

如果说a[l..r]里面全是都是相等的数,那会出现越界的情况(i会增加到r+1,后面会不会继续增加还不知道)

分析4

while(i<j)能不能改成while(i\leqj)?

不能。当执行到i=j的时候,a[i-1](如果存在的话)可能小于x,而a[j+1](如果存在的话)可能大于x,本来这个时候应该停止while循环,但是这里是i\leqj所有a[l...i]里面有大于x的数,a[j...r]里面有小于x的数

结尾

如果要以i为分界点来使用quick_sort函数,则只需修改

void quick_sort(int a[],int l,int r)
{
    if(l>=r)return ;
    int i=l-1,j=r+1;
    int x=a[(i+j)/2];//改成a[(i+j+1)/2]
    
    while(i<j)
    {
        do(i++);while(a[i]<x);
        do(j--);while(a[j]>x);
        if(i<j)swap(a[i],a[j]);
    }
    
    quick_sort(a,l,j),quick_sort(a,j+1,r);//改成quick_sort(a,l,i-1),quick_sort(a,i,r)
}

 完结散花,留个赞再走吧^_^

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值