算法入门(快排,归并,二分详解!)

编码之路,道阻且艰


目录

一、快速排序

 二、归并排序

 三、整数二分

 四、浮点数二分

总结


一、快速排序

思路:分治思想,先随机从数组中取一个数x,然后通过数组中的数与x比较划分左右两个区间,然后递归处理左右两个区间,完成快排。时间复杂度o(n*logn)

 暴力求解做法:用a,b数组分别存小于等于x和大于x的数,然后在各自数组里排序,然后依次将a,b数组的内容存入q中。

题目:

代码:

#include<stdio.h>

void my_qsort(int a[] , int l , int r)
{
    if(l>=r)//如果数组中只有一位或者0位数直接返回,不需要排序
    {return;}
    int lc=l-1,rc=r+1;//取左边界左边一个,取右边界右边一个
    int x=a[l + r >>1];//取中间值,防止超时。
    while(lc<rc)//如果左边的指针小于右边指针就继续迭代
    {
        do lc++;while(a[lc]<x);//从左边界开始遍历,如果找到大于等于x的就停止循环。
        do rc--;while(a[rc]>x);//从右边界开始遍历,如果找到小于等于x的就停止循环。
        if(lc<rc)//如果lc还在rc左边,证明后面循环完,就交换a[rc]与a[lc]的数,因为a[rc]中的数大于x。
        {
            int t=a[lc];
            a[lc]=a[rc];
            a[rc]=t;
        }
    }
    //如果下面这样递归的话,x不能为a[r];
    my_qsort(a,l,rc);//递归遍历左边区间排序。
    my_qsort(a,rc+1,r);//递归遍历右边区间排序。
    //如果上面递归位(a,l,lc-1),下面则应该位(a,lc,r);则上面x不能为a[l],否则死循环;例如样例[1,2];
}

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

 二、归并排序

思路:分治思想,先确定分界点mid=(left+right)/2,然后递归排序left,right,最后归并,合二为一。主要通过双指针实现,两个指针分别执行分组后的最前面。时间复杂度o(n*logn),每层n次,有logn层。

 题目:

 代码:

#include<stdio.h>

void merge_sort(int a[],int l,int r)
{
    if(l>=r) return;
    int mid=(l+r)>>1;//定义一个中间值
    int lc=l,rc=mid+1;//第一个序列从l开始,第二个序列从mid+1开始
    merge_sort(a,l,mid);//递归排序左边的数(第一序列)
    merge_sort(a,mid+1,r);//递归排序右边的数(第二序列)
    int tmp[100010];//定义一个数组存放排序好的数
    int k=0;//数组下标
    while(lc<=mid&&rc<=r)//判断第一序列和第二序列是否全部遍历,是的话退出循环
    {
        if(a[lc]<=a[rc]) tmp[k++]=a[lc++];//如果第一序列的数小就先放入数组
        else tmp[k++]=a[rc++];//否则第二序列的数放入数组
    }
    //上面循环介绍可能没有放完两个序列中一个序列的数。(不相同长度的序列)
    while(lc<=mid) tmp[k++]=a[lc++];//检查第一序列有没有放完,没有就在尾部接着放
    while(rc<=r) tmp[k++]=a[rc++];//同上检查第二序列
    for(int i=l,j=0;i<=r;i++,j++)//最后循环把tmp数组给回a数组,为了上面的递归计算。
    a[i]=tmp[j];
}

int main()
{
    int n;
    scanf("%d",&n);
    int a[100010];
    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;
}

 三、整数二分

思路:

情况一(二分红色边界):判断a[mid]>1,如果不加1的话,可能出现数组两个数(1,2),一直向下取整,l=mid=1,陷入死循环。

情况二(二分绿色边界):判断a[mid]>=x是否在绿色里面,如果是true就证明那个数的位置小于mid,则在红色里面,所以让r=mid;如果是false,则证明那个数在绿色,那个数的位置大于mid的位置,就应该让l=mid+1。

 题目:

代码:

#include<stdio.h>
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    int a[100010];
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    while(m--)
    {
        int x;
        scanf("%d",&x);
        int l=0,r=n-1;
        while(l<r)
        {
            int mid=l+r>>1;
            if(a[mid]>=x) r=mid;//如果a[mid]>=x ,就证明那个数在左边,就缩小右边的距离
            else l=mid+1;//否则缩小左边距离,因为判断过mid了,所以直接从mid+1判断。
        }
        if(a[l]!=x) printf("-1 -1\n");//如果第一次判断了没发现这个数,就证明这个数不在这里面。
        else //发现了这个数再去寻找他的下一个位置
        {
            printf("%d ",l);
            int l=0,r=n-1;
            while(l<r)
            {
                int mid=l+r+1>>1;
                if(a[mid]<=x) l=mid;//上面已经发现mid左边是小于x的了,所以我们判断是否小于x,是就让左边缩小。
                else r=mid-1;//否则让右边缩小,因为已经判断过mid了,直接从mid-1开始判断。
            }
            printf("%d\n",l);
        }
    }
}

 四、浮点数二分

思路:浮点数二分没有整数二分那么麻烦,不需要判断边界条件,所以直接判断大于小于就好了。

题目:

代码:

#include<stdio.h>
int main()
{
    double n;
    scanf("%lf",&n);
    double l=-100,r=100;
    while(r-l > 1e-8)//如果他足够小就退出循环,10的-8次方
    {
        double mid=(l+r)/2;
        if(mid*mid*mid>=n) r=mid;//如果是他的立方大于这个数n,就证明这个位置的数大了,应该在这个数左边,就让r=mid
        else l=mid;//否则在右边,让l=mid
    }
    printf("%lf",l);
    return 0;
}

 

总结

这是博主算法入门的第一课,关于三种基础排序算法的学习,快速排序和归并排序时间复杂度较低,使用起来更快捷,二分查找也对很多题目适用,可以提高我们的编码效率。以上代码和题目均来源于acwing,是博主学习过程中的小笔记,如有侵权,立刻删除。

  • 25
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 40
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员X.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值