编码之路,道阻且艰
目录
一、快速排序
思路:分治思想,先随机从数组中取一个数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,是博主学习过程中的小笔记,如有侵权,立刻删除。