排序与二分

今天,算法篇要续更了。内容的话对于一些算法竞赛是适用的。内容涵盖:

  • 基础算法:排序,二分,前缀和差分等等。
  • 数据结构:栈,队列,堆,哈希等等。
  • 图论与搜索:图的应用+DFS+BFS等等。
  • 贪心算法:区间问题+huffman树等等。
  • 动态规划:线性,区间等等。
  • 数学类:快速幂,博弈论等等。(这个看时间)
    理论补充完后,将补充习题,正文开始。

排序包含:快速排序和归并排序。

快速排序:
1.确定枢轴量x,以及双指针i,j。
2.交换元素,使得左区间元素小于等于x,右区间大于等于x。
3.递归左区间,递归右区间。
算法模板如下:

#include<iostream>
using namespace std;
int q[n]; //存放待排序元素

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);
}

  上面的模板,可以解决所有的边界问题。因此,可以直接用,要是调整边界的话,注意修改的正确性。
归并排序
1.确定边界值mid=(l+r)/2;
2.归并排序左半边,归并排序右半边。
3.归并,将两个有序序列归并为一个序列。
模板:

#include<iostraem>
using namespace std;
int q[n],temp[n]; //q待排序数组;temp辅助数组,是必要的。
void merge_sort(int q[],int l,int r){
    if(l>=r) return;
    int mid=(l+r)/2;
    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];
}

二分

  二分的思想:将区间分为两部分,左半部分满足某种性质,右半部分满足另一种性质。在查找的过程中,一种指针mid快速缩小查找范围,时间复杂度是O(logn)。分为整数二分与浮点数二分,以具体例子降解。:
整数二分:
《数的范围》
  题目附上,直接讲解。根据题目数据量,main函数的时间复杂度不能超过O(nlogn),直接遍历很显然不行,考虑到题目的数据是升序的。自然容易想到二分,无序的话用归并排序,归并排序的时间复杂度是O(nlogn)。这样也不超时。AC代码如下:

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=1e6 + 5;
int a[maxn],n,q;
int main(){
    cin>>n>>q;
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    while(q--){
        int k;
        scanf("%d",&k);
        int l=0,r=n-1;
        while(l<r){
            int mid = l + r  >> 1 ;
            if(a[mid]<k) l=mid+1;
            else r=mid;
        }
        if(a[l]!=k) cout<<"-1 -1"<<endl;
        else{
            cout<<l<<" ";
            int l=0,r=n-1;
            while(l<r){
                int mid = (l + r + 1) >> 1 ;
                if(a[mid]>k) r=mid-1;
                else l=mid;
            }
           cout<<r<<endl;
        } 
    }
    return 0;
}

  重要的是模板,整数二分有两套模板:

//假定 区间左边的数a满足的性质为: a<=x;区间右边的数b满足的性质为: b>x;
// 我们的目标是找x,它在q中。
int q[n]; //q中,拥有n个数,升序排列。
int l = 0,r= n-1; //区间左右边界
while(l<r){
    int mid = l + r >> 1;
    if(q[mid]<=x) l = mid;
    else r = mid -1 ;
}
//假定 区间左边的数a满足的性质为: a<=x;区间右边的数b满足的性质为: b>x;
// 我们的目标是找x,它在q中。
int q[n]; //q中,拥有n个数,升序排列。
int l = 0,r= n-1; //区间左右边界
while(l<r){
    int mid = l + r + 1 >> 1;
    if(q[mid]>=x) r = mid;
    else l = mid + 1 ;
}

仔细比较两者区别,合理使用。这两套模板可以解决所有的边界问题。
浮点数二分:
浮点数二分,好处是没有边界问题,比较简便,但是要注意精度。
数的三次方根
直接上AC代码:

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
int main(){
    double n ;
    cin>>n;
    double l=0, r=abs(n);
    if (r<=1) r=1; 
    while((r - l) > 1e-8){
        double mid = (l + r) / 2;
        if(mid*mid*mid > abs(n)) r = mid ;
        else l = mid;
    }
    if (n>=0) printf("%.6f",r);
    else printf("%.6f",r*-1);
    return 0;
}

点一下用到的技巧:
1.正负数统一处理,将输入的负数转变为正数,输出时记得判断符号。
2.注意在[0,1]区间上,x三次方根大于x。注意将搜索区间扩大到l=0,r=1。不然会出错。
模板如下:

//假定 区间左边的数a满足的性质为: a<=x;区间右边的数b满足的性质为: b>x;
// 我们的目标是找x的平方根,其中x>1。
double l = l_bound,r= r_bound; //区间左右边界
while( (r-l) > 1e-8){
    double mid = (l + r) / 2 ;
    if(mid * mid >=x) r = mid;
    else l = mid;
}

主要是注意精度问题,小数点保留6位,就需要精度为1e-8,4位就1e-6。以此类推。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值