algorithm快速排序

algorithm快速排序

快速排序是排序算法中平均时间复杂度为o(nlog n)的一种算法。主要思想如下:

  1. 找一个基准数x,这个数可以是数组中存在的任何一个数,一般取q[l],q[r],q[l+r>>1],这三种情况。
  2. 分集合,左边集合的数都小于等于基准数x,右边集合的数都大于等于x。

不是把小于等于x的数放在x的左半边,大于等于x的数放在x的右半边。这两种说法是有区别的,不要混淆,例如一个数组的所有数都是1这种情况,应该能明白把。

  1. 分别对左边的集合和右边的集合进行上述步骤,结束条件:左边界等于右边界,即集合中只有一个数了。

看代码:

#include<iostream>
#include<algorithm>
using namespace std;

const int N=100000;
int q[N];
void quick_sort(int q[], int l, int r)
{
    if(l>=r) return;
    int i=l-1,j=r+1,x=q[l+r>>1];
    while(i<j)
    {
        do i++;while(q[i]<x);//小于x直接跳过  直到找到一个大于等于x的数 
        do j--;while(q[j]>x);//大于x直接跳过  直到找到一个小于等于x的数
        if(i<j) swap(q[i],q[j]);  //交换 上面找到的数
    }
    quick_sort(q,l,j),quick_sort(q,j+1,r);
}

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)scanf("%d",&q[i]);
    int l=0,r=n-1;
    quick_sort(q,l,r);
    for(int i=0;i<n;i++)printf("%d ",q[i]);
    return 0;
}

看下实际模拟的情况:

10个数
49 59 88 37 98 97 68 54 31 3

可以看到基准数q[4]=98,模拟一下第一趟排序,左边集合的个数都小于等于98,右边都大于等于98.
初始时 i和 j 都往两边扩展了一位,不管3*7=21,代码中用的do while循环,开始时i++,j–。
在这里插入图片描述

这样左边集合就是 49 59 88 37 3 97 68 54 31
右边集合是 98

j的位置最终指向了左边集合的最后一个位置。但是i的最终位置是不确定的,有可能是右边集合的第一个位置,也有可能是左边集合的最后一个位置。 记住j的位置,这是分界点的位置。

关于代码

上面的代码肯定会有很多人不理解这样的写法,如果有同学觉得有问题,可以私信我,后面我会专门写一篇文章,写一下上面代码的出错的各种临界情况。

给定一个长度为n的整数数列,以及一个整数k,请用求出数列从小到大排序后的第k个数。 输入格式 第一行包含两个整数 n 和 k。

第二行包含 n 个整数(所有整数均在1~109范围内),表示整数数列。
输出格式 输出一个整数,表示数列的第k小数。

数据范围 1≤n≤100000, 1≤k≤n

输入样例:

5 3
2 4 1 5 3

输出:

3

快速排序的思想是对集合划分成两个子集合,左边集合都小于等于x,右边集合都大于等于x,如果左边集合的个数恰好为k,那么左边集合的最后一个数就是第k小的数。

#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010;
int q[N];
int  quick_sort(int l,int r,int k)
{
    if(l==r) return q[l];
    int i=l-1,j=r+1,x=q[l+r>>1];
    while(i<j)
    {
        do i++;while(q[i]<x);
        do j--;while(q[j]>x);
        if(i<j) swap(q[i],q[j]);
    }
    
    int s=j-l+1;
    if(k<=s) quick_sort(l,j,k);
    else  quick_sort(j+1,r,k-s);
}
int main()
{
    int n,k;
    cin>>n>>k;
    for(int i=0;i<n;i++) cin>>q[i];
    cout<<quick_sort(0,n-1,k);
}

再看一个题,是上道题的应用。

给定一个由整数组成的集合,集合中的整数各不相同,现在要将他分成两个集合,使得这两个集合交际为空集,并集为原集合,同时在两个子集合的元素个数n1与n2之差的绝对值|n1-n2|尽可能小的情况下,要求他们各自的元素之和s1与s2之差的绝对值|s1-s2|尽可能大。并输出这个差

一组样例

13
1 6 33 18 4 0 10 5 12 7 2 9 3
输出 80

其实思路很简单,根据上一题的做法,我们很简单的可以得到一个集合中第k小的数,这个题目要求集合元素数目差尽可能小,和的差尽可能大。常规情况下,我们自然可以想到将这组数据排序后,取前一半元素和后一半元素构成两个集合,这样数目差最大为1(奇数个数据相差为1,偶数个数相差为0),和的差必然是最大的。
我们学会了上一题的做法后,就不用进行排序了,因为我们可以对集合进行划分,找到第n/2小的数,左边为一个集合,右边为一个集合,时间复杂度就可以降低,期望复杂度为o(n),而排序复杂度最好也是o(nlogn)。

#include<bits/stdc++.h>
using namespace std;
const int N=100000;
int q[N];
void quick_sort(int l, int r,int k)
{
    if(l>=r) return;
    int i=l-1,j=r+1,x=q[l+r>>1];
    while(i<j)
    {
    	while(q[++i]<x) ;
    	while(q[--j]>x) ;
        if(i<j) swap(q[i],q[j]);
    }
    int s=j-l+1;
    if(k<=s) quick_sort(l,j,k);
    else quick_sort(j+1,r,k-s);
}

int main()
{
    int n;
    scanf("%d",&n);
    int sum=0,sum1=0;
    for(int i=0;i<n;i++)
	{
		scanf("%d",&q[i]);sum+=q[i];
	}
    int l=0,r=n-1;
    quick_sort(l,r,n/2);
    for(int i=0;i<n/2;i++)
    	sum1+=q[i];

    cout<<sum-sum1-sum1;
    return 0;
}


//13
//1 6 33 18 4 0 10 5 12 7 2 9 3
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我定亲手擦亮

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

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

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

打赏作者

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

抵扣说明:

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

余额充值