算法基础一排序与二分

算法学的实在是太la了,所以就要从头开始再来一遍,加油!

快排和归并都用到了分治的思想

快速排序

  最开始,随机选取一个值x作为分界点,一般是选择数组下标为中间值的那个数(即x=q[l+r>>1]),用到双指针i和j,i指向最左端元素的左边一个位置(即i=l-1),j指向最右端元素的右边一个位置(即j=r+1),接下来开始指针移动,满足q[i]<x,i++;满足q[j]>x,j--,就这样一直移动,直到i和j相遇,有些解释看下边的快排代码模板。

代码模板如下:

void quick_sort(int q[],int l,int r)
{
    if(l>=r) return;
    
    int x=q[l+r>>1],i=l-1,j=r+1;//l+r>>1等价于(l+r)/2;
    while(i<j)
    {
        do i++;while(q[i]<x);//注意里面不能等于,不然可能会出问题
        do j--;while(q[j]>x);
        if(i<j) swap(q[i],q[j]);/*要满足x左边的数都小于x,x右边的数都大于x,所以在上边i和j都
停下来时(就是说i指向的数大于或等于x了,j指向的数小于或等于x了),就要交换q[i]和q[j],
保证满足上边的条件。*/
    }
    
    quick_sort(q,l,j);//虽然说是分左右两段在排,但是这边用的是j,因为有些情况下i会错误,而且y总的模板还是要得背会
    quick_sort(q,j+1,r);
}

下面是一道例题:785. 快速排序 - AcWing题库

#include<iostream>
using namespace std;
const int N=1e6+10;

void quick_sort(int q[],int l,int r)
{
    if(l>=r) return;
    
    int x=q[(l+r)/2],i=l-1,j=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 n,q[N];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&q[i]);
    quick_sort(q,1,n);
    for(int i=1;i<=n;i++) printf("%d ",q[i]);
    return 0;
}

 

归并排序:

最开始,找下标为中间值的数作为分界点(即x=q[l+r>>1]),接着分别递归排序左边,右边,即merge_sort(q,l,mid),merge_sort(q,mid+1,r),最后归并,这里边还是需要用到两个指针i,j,另外需要定义一个临时数组temp[],还有该数组下标,i指向第一段的开头,j指向第二段的开头,比较q[i]和q[j]的大小,先把小的放在temp里面,然后指针向后挪,直到有一个指针到头了,就停止。最后把还没有填入temp中的数给填进去。最后一步就是把temp里的数给物归原主到q里边。

代码模板如下:

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];//事实上,这个一直没整大明白
}

下面是一道例题:787. 归并排序 - AcWing题库

#include<iostream>
using namespace std;
const int N=1e5+10;

int n;
int q[N];
int temp[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]) temp[k++]=q[i++];
        else temp[k++]=q[j++];
    }
    while(i<=mid) temp[k++]=q[i++];
    while(j<=r) temp[k++]=q[j++];
    
    for(int i=l,j=0;i<=r;i++,j++) q[i]=temp[j];
}

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

 

二分:

(1)整数二分:

模板很重要,二分有两个模板,(二分是一定有解的,但是题目不一定有解)要知道如何选择。如果有单调性一定可以二分,但可以二分的题目不一定非得有单调性,事实上二分的本质是将整个区间一分为二,找到一个性质是一半满足,一半不满足。关键是区间边界更新,先写一个check函数,根据这个函数看如何更新区间,如果更新区间为l=mid,r=mid+1那么前面mid要写(l+r+1)/2(加1是怕死循环);如果更新区间为r=mid,l=mid+1,那么前面mid就是(l+r)/2。事实上这个二分还真得自己悟,就是更新区间的问题,多做题。

下面是一道例题:789. 数的范围 - AcWing题库

#include<iostream>
using namespace std;
const int N=1e5+10;

int n,m,q[N],x;

int main()
{
    int mid;
    scanf("%d %d",&n,&m);
    for(int i=0;i<n;i++) scanf("%d",&q[i]);
    while(m--)
    {
        scanf("%d",&x);
        int l=0,r=n-1;
        while(l<r)
        {
            mid=(l+r)/2;
            if(q[mid]>=x) r=mid;
            else l=mid+1;
        }
        if(q[l]!=x) cout<<"-1 -1"<<endl;
        else
        {
            cout<<l<<' ';
            int l=0,r=n-1;
            while(l<r)
            {
                mid=(l+r+1)/2;
                if(q[mid]<=x) l=mid;
                else r=mid-1;
            }
            cout<<l<<endl;
        }
    }
    
    return 0;
}

(2)浮点数二分:

浮点数比较简单,因为他不用处理边界

下面是一道例题:790. 数的三次方根 - AcWing题库

#include<iostream>
using namespace std;
int main()
{
    double n;
    cin>>n;
    double l=-10000,r=10000;
    while(r-l>1e-8)
    {
        double mid=(l+r)/2;
        if(mid*mid*mid>=n) r=mid;
        else l=mid;
        
    }
    cout<<l<<endl;
    return 0;
}

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值