基础算法(一)

基础算法(一)

快速排序

(时间复杂度nlog2(n)
——一共分成log2n层,每层比较n次)

思想–分而治之
1)在区间内任意选择一个数x作为分界点
2)将小于x的数全部转移到分界点左边
将大于x的数全部转移到分界点右边
方法:
创建下标标记使其指向数组左右两边
i=l-1
j=r+1
第二步使i,j分别向数组中心移动
倘若i指向的数>=x则i停下等待
j指向的数<=x则j停下等待
第三步交换i,j指向的两个数

直到i和j相遇
3)递归处理左右两边

#include<iostream>

using namespace std;

const int N =1e6+10;

int q[N];

int n;

void qucik_sort[int q[],int l,int r]{

if(l>=r)return;

int i=l-1, j=r+1,x=q[l+r>>1];//尽量选择中间作为x值,这样每次都分一半左右递归,这样一共分logn层,如果取左右端点的话就会分成n层,每次比较n个数复杂度会变成n^2

while(i<j){

while(q[++i]<x);//此处特别注意不能写为<=x和>=x,如果等于不交换的话,可能会一直保持x的位置不动,出现错误的排序

while(q[--j]>x);

if(i<j)swap(q[i],q[j]);

}

quick_sort(q,l,j);

quick_sort(q,j+1,r);//若以i作为边界则是quick_sort(q,l,i-1);quick_sort(q,i+1,r)

}
int main (){

scanf("&d",&n);

for (int i=0;i<n;i++)scanf("%d",&q[i]);

quick_sort(q,0,n-1);

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

return 0;
}

当给定的序列有序时,如果每次选择区间左端点进行划分,每次会将区间[L, R]划分成[L, L]和[L + 1, R],那么相当于每次递归右半部分的区间长度只会减少1,所以就需要递归 n−1次了,时间复杂度会达到 n^2。但每次选择区间中点或者随机值时,划分的两个子区间长度会比较均匀,那么期望只会递归 logn层–yxc

//写道例题
在这里插入图片描述

#include<iostream>

using namespace std;

const int N=1e5+10;

int q[N];

int n,k;

int quick_sort(int q[],int l,int r,int k){

int i=l-1,j=r+1,x=q[l+r>>1];

if(i>=j)return q[l];

while(i<j){

while(q[++i]<x);

while(q[--j]>x);

if(i<j)swap(q[i],q[j]);

}
if((j-l+1)>=k)return quick_sort(q,l,j,k);

else return quick_sort(q,j+1,r,k-(j-l+1));

}
int main(){

scanf("%d %d",&n,&k);

for(int i=0;i<n;i++)scanf("%d",&q[i]);

printf("%d",quick_sort(q,0,n-1,k);)

return 0;
}

//这个题我觉得一般人就直接输出q[k-1]了吧…反正菜鸡我是这样
看了yxc大佬的代码果然大佬还是强,,,可能只是常规操作
这样写就直接减了一半的运算时间,复杂度降成了n(1+1/2+1/4+1/8…)<=2n
时间复杂度为O(n);

归并排序

(时间复杂度nlog2(n)
——一共分成log2n层,每层比较n次)

思想—分治
1)选择中间的数x((l+r)/2)作为分界点
2)递归处理左右两边
3)将左右两边的数组合二为一
分别定义两下标指向左右两端数组
依次向后作比较
将比较的较小值加入新的数组

两数相同时则加入第一个数组的值
比较完成得到新的数组

#include<iostream>

using namespace std;

const int N =1e5+10;

int q[N],temp[N];

int n;

void merge_sort (int q[],int l,int r){

if(l>=r)return;

int mid =l+r>>1;

int i=l,j=mid+1;                 //注意这里j必须取mid+1因为/2向下取整,j就尽量往上取,如果j取成mid~r,i取成l~mid-1就会有边界问题   

int k=0;

merge_sort(q,l,mid);

merge_sort(q,mid+1,r);//递归处理左右两边

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(i=l,k=0;i<=r;i++,k++)q[i]=temp[k];//尤其注意这一行,在利用temp数组向q赋值时,应注意temp下标从零开始,而q数组应从l取值到r

}

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;

}

//例题
在这里插入图片描述
这里是菜鸡本人的解法,没什么好说的,逐个比较,果不其然超时。。。

#include <iostream>

using namespace std;

const int N=1e5+10;

int q[N];

int n;

static long long  x=0;

void   method(int q[],int l,int r){
   if(l>=r)return;
    int mid;
    mid=l+r>>1;
    method(q,l,mid);
    method(q,mid+1,r);
    
    for(int i=l;i<=mid;i++){
        for(int j=mid+1;j<=r;j++)
        if(q[i]>q[j])x++;
        
    }
    
}

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

下面是yxc大佬的解法
简单叙述一下思路
首先还是一样的利用归并排序进行排序
在中间比较的时候采用一些技巧对逆序对累和
还是强?手写一遍表示敬意

#include<iostream>

using namespace std;

const int N =1e5+10;

int n;

int q[N],temp[N];

typedef long long LL;

LL merge_sort(int q[],int l,int r){

if(l>=r)return 0;

int mid=l+r>>1;

int i=l,j=mid+1,k=0;

LL res =merge_sort(q,l,mid)+merge_sort(q,mid+1,r);//递归处理左右两边,得到的逆序对进行累加
while(i<=mid&&j<=r){

if(q[i]<=q[j])temp[k++]=q[i++];

else{

temp[k++]=q[j++];

 res+=mid-i+1;//*********倘若q[i]>q[j]那么i到mid的数也大于q[j],,逆序对即为mid-i+1个
}

}

while(i<=mid )temp[k++]=q[i++];

while(j<=r)temp[k++]=q[j++];//扫尾处理

for(i=l,k=0;i<=r;i++,k++)q[i]=temp[i];//排序完成,将排得的数复制到原数组当中

return res;
}

int main(){

scanf("%d",&n);

for(int i=0;i<n;i++)scanf("%d ", &q[i] );

printf("%ld",merge_sort(q,0,n-1));

return 0;

}

二分查找法(整数二分)

  1. 选择中间数mid–作为“性质“性质一定存在于符合条件的区间内
  2. check(mid)写判断依据作为更新区间的条件
  3. 更新区间
  • 模板 1.(找左边界
      c h e c k > = x m i d = ( l + r ) / 2 i f — t u r e r = m i d f a l s e l = m i d + 1 \ check >=x \\ mid=(l+r)/2 \\ if—ture \qquad r=mid \\ false \qquad l=mid+1  check>=xmid=(l+r)/2ifturer=midfalsel=mid+1

  • 模板2.(找右边界
      c h e c k < = x m i d = ( l + r + 1 ) / 2 i f — t r u e l = m i d f a l s e r = m i d − 1 \ check <= x \\ mid=(l+r+1)/2 \\ if—true \qquad l=mid \\ false \quad r=mid-1  check<=xmid=(l+r+1)/2iftruel=midfalser=mid1

(如果mid=(l+r)/2当r=l+1并以true更新边界时,会进入死循环,,例如x=1,q[1]=1,l=1,r=2更新完之后仍然是l=1,r=2)

在这里插入图片描述

所谓的二分算法,就是我们知道当前的候选区间中,一定存在我们要找到的答案,而且我们发现这个区间拥有单调性质此类的性质,那么我们可以不停地缩减候选区间的范围,达到排除无用答案的效果

在这里插入图片描述

#include  <iostream>

using namespace std;

const int N =1e5+10;

int q[N];

int n,k;

int main(){

scanf("%d %d",&n,&k);

for(int i=0;i<n;i++)scanf("%d",&q[i]);

while(k--){

int x;

scanf("%d",&x);

int l=0,r=n-1;//定义初始的左右边界

while(l<r){

int mid=l+r>>1;

if(q[mid]>=x)r=mid;    //此时目标是寻找左边界,因为是整数二分,如果不存在目标值,会找到小于x的最大值

else l=mid+1;

}
if(q[l]!=x)printf("-1 -1\n");

else  printf("%d ",l);

int l=0,r=n-1;//重新初始化l,r,找右边界
while(l<r){

int mid=l+r+1>>1;//当更新边界是l=mid时需要+1否则会造成边界问题

if(q[mid]<=x)l=mid;

r=mid-1;
}
printf("%d",l);


}

return 0;
}

浮点数二分

不用考虑边界问题
直接迫近即可
在这里插入图片描述

#include <iostream>
using namespace std;

//double 能精确到小数点后15——16位,而float只能精确到6-7位是不太够的
int main(){

double  x;

cin>>x>>endl;

//选区间时只要包含答案即可(注意右边界必须大于一,否则求(0,1)区间会越取越大爆范围,只要右区间大于1即可)
double l=-100,r=100;

while(r-l>1e-8)//精确到1e-8
{
double mid=(l+r)/2;

if(mid*mid*mid>=x)r=mid;   //因为区间内包含所有数据(不只是整数还有全部小数)所以数据不存在间断,此时用左右模板均可,并且不用考虑边界问题

else l=mid;

}
printf("%6lf",l);

return 0;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值