2021-02-10

快速排序-分治

1.确定分界点:q[l] q[r] q[(l+r)/2] q[random]
2.*调整范围,使得左边一部分的数都<=分界点,右边部分的数都>=分界点
3.递归处理左右两段使得两段排好序,也就排好了整体

#include <iostream>
#include <cstdio>

using namespace std;

const int N = 100010;
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);
        do j--; while(q[j]>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]);
    }

    quick_sort(q,0,n-1);  //注意0、n-1
    for(int i = 0; i < n; i++)
    {
        printf("%d ",q[i]);
    }

    return 0;
}

归并排序-分治

1.确定分界点:mid=l+r>>1
2.递归排序左边部分和右边部分
3.归并-合二为一(即将原来左右两部分各自排好的序整合成一大部分排好的序)

#include <iostream>

using namespace std;

const int N = 1000010;

int a[N],tmp[N];

void merge_sort(int q[],int l,int r)
{
    if(l>=r) return;
    int mid = l+r>>1;
    merge_sort(q,l,mid),merge_sort(q,mid+1,r);  //左右两边排好序
    int k = 0,i=l,j=mid+1;
    while(i<=mid && j<=r)
        if(q[i]<=q[j]) tmp[k++]=q[i++];
        else tmp[k++]=q[j++];
    while(i<=mid) tmp[k++]=q[i++];
    while(j<=r) tmp[k++]=q[j++];

    for(i=l,j=0;i<=r;i++,j++) q[i]=tmp[j];//复制回q[]里头
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 0; i < n; i++) scanf("%d",&a[i]);
    merge_sort(a,0,n-1);

    for(int i = 0;i<n; i++)
        printf("%d ",a[i]);
    return 0;
}

快速选择

类似于快排,但是他只用递归左半部分或右半部分
例题:
给定一个长度为n的整数数列,以及一个整数k,请用快速选择算法求出数列从小到大排序后的第k个数。

输入格式
第一行包含两个整数 n 和 k。

第二行包含 n 个整数(所有整数均在1~109范围内),表示整数数列。

输出格式
输出一个整数,表示数列的第k小数。

数据范围
1≤n≤100000,
1≤k≤n
输入样例:
5 3
2 4 1 5 3
输出样例:
3

#include <iostream>

using namespace std;

const int N=100010;
int q[N];
int n,k;

int quick_sort(int l,int r,int k)
{
    if(l==r) return q[l]; //区间只有一个数时就是要求的数
    int x=q[l],i=l-1,j=r+1;;
    while(i<j)
    {
        while(q[++i]<x);
        while(q[--j]>x);
        if(i<j) swap(q[i],q[j]);
    }
    int sl=j-l+1;
    if(k<=sl) quick_sort(l,j,k); //第k个数比划分的左半部分的个数少时,必定在左边,所以只要递归左边
    else quick_sort(j+1,r,k-sl);  //右边同理
}
int main()
{
    cin >> n >> k;
    for(int i = 0; i < n; i++) cin >> q[i];
    cout << quick_sort(0,n-1,k)<<endl;
    return 0;
}

二分

如果有单调性一定可以二分,可以二分的题目不一定非要有单调性,所以二分的本质并不是单调性。

整数二分(注意边界问题)

//模板一
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    return l;
}

//模板二
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

分析-Acwing shibagugi用户、Miramo用户

假设有一个总区间,经由我们的 check 函数判断后,可分成两部分
我们以o作 true,…作 false 示意较好识别
若我们的目标是下面这个v,那麽就必须使用模板 1

…vooooooooo

假设经由 check 划分后,整个区间的属性与目标v如下,则我们必须使用模板 2

oooooooov…

所以下次可以观察 check 属性再与模板1 or 2 互相搭配就不会写错啦
模板1就是在满足chek()的区间内找到左边界,模板2在满足check()的区间内找到右边界。然后无论是左边界还是右边界,都应该是整个区间中某一段满足某性质(如单调不降)与另一段不满足该性质的分界点(也就是同学的v)

实数二分(不存在边界问题)

bool check(double x) {/* ... */} // 检查x是否满足某种性质

double bsearch_3(double l, double r) {
    // 定义一个 eps, 处理精度问题
    const double eps = 1e-6;   
    // 注意循环条件不要写错
    while (r - l > eps) {
        // 步骤 A: 找中间值
        double mid = (l + r) / 2;
        // 步骤 B: 判断是否满足性质
        if (check(mid)) r = mid;
        else l = mid;
    }
    return l;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

紫薯C菌

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

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

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

打赏作者

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

抵扣说明:

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

余额充值