归并排序、快速排序、逆序对

洛谷 P1177 【模板】快速排序

(1)归并排序

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,a[N],b[N];
void merge_sort(int l,int r) // 归并排序
// 递归过程类似于树的后序遍历,先把左、右子区间均完成排序后,再合并当前区间
{
    if(l==r)return; // 单个数字,已经有序,直接返回
    int mid=(l+r)/2;
    merge_sort(l,mid); //向下递归左区间[l,mid]
    merge_sort(mid+1,r); //向下递归右区间[mid+1,r]

	// 此时,左右子区间均已有序
    // 合并左右子区间并保持有序,之后向上返回
    int p1=l,p2=mid+1; // p1是左区间指针,p2是右区间指针
    for(int i=l;i<=r;i++) // 合并两个有序的线性表,存于辅助数组b[i]
    {
        if(p1<=mid&&(a[p1]<=a[p2]||p2>r)) b[i]=a[p1++];
        else b[i]=a[p2++];
    }
    for(int i=l;i<=r;i++) // b[i]赋回给a[i]
        a[i]=b[i];
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    merge_sort(1,n); //135ms
    //sort(a+1,a+n+1); //148ms
    for(int i=1;i<=n;i++)
        i==n?printf("%d\n",a[i]):printf("%d ",a[i]);
    return 0;
}
/*
5
5 4 3 2 1
*/

当L=1,R=5时的递归树:(电脑不好画,直接手写了)
在这里插入图片描述
(2)快速排序

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,a[N];
int partion(int p,int r)
{
    int i=p,j=r+1;
    int x=a[p];
    while(1)
    {
        while(a[++i]<x&&i<r);
        while(a[--j]>x);
        if(i>=j)break;
        swap(a[i],a[j]);
    }
    a[p]=a[j];
    a[j]=x;
    return j;
}
void qsort(int l,int r)
{
    srand(time(0));
    if(l<r)
    {
        int pos=rand()%(r-l+1)+l; // 在[l,r]内的随机数,以它为基准
        swap(a[l],a[pos]);
        int q=partion(l,r);
        qsort(l,q-1);
        qsort(q+1,r);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    qsort(1,n);
    for(int i=1;i<=n;i++)
        i==n?printf("%d\n",a[i]):printf("%d ",a[i]);
    return 0;
}
/*
5
5 4 3 2 1
*/

《数据结构》书上的代码写法(最后一个数据点需要开O2优化才能过):

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,a[N];
int get_key(int l,int r)
{
    int key=a[l];  // 枢轴,此处的选择影响时间复杂度
    while(l<r)
    {
        while(l<r&&a[r]>=key)r--; // 找到右边第一个小于枢轴的位置r
        a[l]=a[r]; // 将其填充给a[l]
        while(l<r&&a[l]<=key)l++; // 找到左边第一个大于枢轴的位置l
        a[r]=a[l]; // 将其填充给a[r]
    } // 退出时 l=r
    a[l]=key; // 下一次递归的枢轴位置为l,值为key
    return l; // 返回下一次递归的枢轴
}
void quick_sort(int l,int r)
{
    srand(time(0));
    if(l<r)
    {
        int pos=l+rand()%(r-l+1); // 随机产生一个[l,r]之间的数
        swap(a[l],a[pos]); // 将其与当前区间的第一个数交换
        int mid=get_key(l,r); // mid将区间分成两部分
        quick_sort(l,mid-1);
        quick_sort(mid+1,r);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    quick_sort(1,n);
    for(int i=1;i<=n;i++)
        i==n?printf("%d\n",a[i]):printf("%d ",a[i]);
    return 0;
}
/*
5
5 4 3 2 1
*/

洛谷 P1908 逆序对

归并排序的应用:求逆序对
(其实只是在else那里加了一句代码)

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,a[N],b[N];
long long ans;
void merge_sort(int l,int r) // 归并排序
{
    if(l==r)return; 
    int mid=(l+r)/2;
    merge_sort(l,mid); 
    merge_sort(mid+1,r); 
    int p1=l,p2=mid+1;
    for(int i=l;i<=r;i++) 
    {
        if(p1<=mid&&(a[p1]<=a[p2]||p2>r)) b[i]=a[p1++];
        else 
        {	
        	b[i]=a[p2++];
        	ans+=mid-p1+1; // 只是在这里加了一句代码
        }
    }
    for(int i=l;i<=r;i++) 
        a[i]=b[i];
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    merge_sort(1,n); 
    printf("%lld\n",ans);
    return 0;
}
/*
4
3 3 2 2

5
5 4 3 2 1
*/

当然也可以直接上 树状数组+离散化 求:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+10;
ll ans,tr[N];
int n,cnt,a[N],t[N];
void add(int i)
{
    while(i<=n)
    {
        tr[i]++;
        i+=(i&-i);
    }
}
ll sum(int i)
{
    ll s=0;
    while(i)
    {
        s+=tr[i];
        i-=(i&-i);
    }
    return s;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        t[i]=a[i];
    }
    sort(t+1,t+n+1);
    cnt=unique(t+1,t+n+1)-t-1;//是-t-1
    for(int i=1;i<=n;i++)
    {
        a[i]=lower_bound(t+1,t+cnt+1,a[i])-t;//是-t,不是-t-1
        add(a[i]);
        ans+=sum(n)-sum(a[i]);
    }
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

nefu-ljw

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

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

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

打赏作者

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

抵扣说明:

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

余额充值