被会长要求学快排和归并,基础太差,上网看别人的代码又看不懂。不能自强就只能坐金团人带呗。找本本学了一下午的排序。
先补了前置知识——数组。
数组
就是基本的定义数组,给数组赋值,输出数组的值
include<stdio.h>
int main(){
int c,b;//b长度
printf("输入数组长度");
scanf("%d",&b);
int a[b];
for(c=0;c<b;c++){//给数组赋值
scanf("%d",&a[c]);
}
for(i=0;i<=b;i++){//输出
printf("%d",a[i]);
}
我觉得直接学快排和归并有点困难,所以先学的冒泡排序。
冒泡排序
冒泡排序原理很简单,循环比较相邻两个数的大小,每一轮都可以选出一个最大的数放在最后面。
没什么好讲的。就注意一下,每一轮最后一位数不用比就好了。
#include<stdio.h>
int main(){
int c,b;//b是长度
printf("输入数组长度");
scanf("%d",&b);
int a[b];
for(c=0;c<b;c++){//给数组赋值
scanf("%d",&a[c]);
}
int i,j,l;//i记位置,
for(i=0;i<b-1;i++){
for(j=0;j<b-1-i;j++){//最后一位数不用再比
if(a[j]>a[j+1]){
l=a[j+1];//交换
a[j+1]=a[j];
a[j]=l;
}
}
}
for(i=0;i<=b-1;i++){//输出
printf("%d ",a[i]);
}
return 0;
}
快排
先说原理吧,我自己的理解:先把a[0]作为key,再从you(初始是最后一位)开始从右往左寻找小于key的数。复制到zuo(a[0])的位置。再从zuo往右寻找大于key的数赋值给you。每次赋值左右都会往中间移动,一直循环直到zuo you相交,把key的值插[zuo最后的位置。于是左边的数都比标准值小,右边的数都比标准值大,然后再从key往左右缩小区间递归。
下面是具体操作
int f(int a[],int zuo,int you){
int key=a[zuo];//取最左边的数为标准值
while(zuo<you){//左右碰撞跳出循环,再把 key赋值给相交的位置
if(zuo<you&&a[you]>=key){
you--;
}
a[zuo]=a[you];
if(zuo<you&&a[zuo]<=key){
zuo++;
}
a[you]=a[zuo];
}
a[zuo]=key;
return zuo;//返回key的位置
}
这里要重点讲一下具体过程,先定义zuo,you为数组左右两端的位置。
先取最左边的a[0]为key,用key来保存a0的值,a0就变成了一个可以放东西的空位。然后我们从you开始看,我们要先找一个小于key的数,
如果you位置的值大于key,you就往左移一格,无论移后you的值是否小于key,都将它复制到zuo的位置。如果you位置的值小于key,就正合我们意,直接复制到zuo的位置。
然后我们开始看zuo的位置,如果刚刚移过来的还是一个大于key的数,那么他会被扔回去。所以不用担心刚刚扔来一个大于key的数。如果刚刚来的是一个小于key的数,那就不需要管他了,zuo的位置往右移一格,继续开始判断,跟you的情况类似。
一直循环到zuo you的位置重合,跳出while循环,把key插回重合位置,就实现了key左边全部比key小,右边全部比key大。
然后开始转换研究区间。先看如下代码:
void g(int a[],int zuo,int you){
int mid;
if(zuo<you){
mid=f(a,zuo,you);
g(a,zuo,mid-1);//缩小区间开始递归
g(a,mid+1,you);
}
这个是转移区间的代码,定义mid为刚刚zuo you重合的位置,开始转移区间至 zuo到mid-1,左边弄完后,转移至 mid+1到you。递归完就排好啦。
主函数部分就是输入和输出部分,没什么好讲的。
#int main(){
int c,b;//b长度
printf("输入长度\n");
scanf("%d",&b);
int a[b];
for(c=0;c<b;c++){
scanf("%d",&a[c]);//数组赋值
}
g(a,0,b-1);//开始
for(c=0;c<b;c++){
printf("%d ",a[c]);//输出结果
}
return 0;
}
归并排序
然后开始最难的归并排序。先说一点坑人的地方吧,很多地方都说归并是先将数组一直,尽量相等的拆成两份,拆到最后只剩一两个再开始排、合并。其实不是拆数组,而是缩小研究区间。可能是我太菜了才会理解错吧。
然后说原理吧,将数组分成长度尽量相等的两份,递归至每份里的元素小于等于2个。
然后取最左边的两份,然后单独排每份,排好后,创建临时数组用于合并。定义每份第一位数分别为
one two 比较one two 并塞入临时数组,移动one two 的位置循环,然后把剩下的数直接塞进
临时数组,然后再把临时数组赋值给原数组对应的区间。一直递归就排好咯,代码比较复杂,一点一点看吧。
#void f(int a[],int zuo,int you){
int mid=(zuo+you)/2;//分数组
if(you-zuo>1){//分数组至每份元素少于等于2个时结束
f(a,zuo,mid);//左递到底然后排,右边递到底然后排,然后合并这两个。再归
f(a,mid+1,you);
hebing(a,zuo,you,mid);
}
else{//排两个元素
if(a[zuo]>a[you]){
int q;
q=a[zuo];
a[zuo]=a[you];
a[you]=q;
}
}
}
先定义个mid长度用于分数组,长度为奇数的话,左边那份会少一个(int去尾)。然后从左开始递归分数组,最后元素少于等于两个时结束,进入else部分排两个元素的大小,比较大小交换元素,没什么好说的。然后我们开始看合并部分的代码
#void hebing(int a[],int zuo,int you,int mid){//合并函数
int i=0;
int b[you+1-zuo];//临时数组b的长度为两个区间长度总和
int one=zuo,two=mid+1;
while(one<=mid&&two<=you){
if(a[one]>a[two]){
b[i]=a[two];
two++;
i++;
}
else{
b[i]=a[one];
i++;
one++;
}
}
while(one<=mid){//另一组塞完后,这一组剩下的直接放到后面
b[i]=a[one];
one++;
i++;
}
while(two<=you){//与上面同理
b[i]=a[two];
two++;
i++;
}
int j;
for(j=0;j<i;j++){
a[zuo+j]=b[j];//临时数组代回原数组
}
}
分完到最后呢定义左边数组的第一位为one,mid右边一位就是右边数组第一位,定义为two
合并的时候,先定义一个临时数组b【】,长度是合并的两个数组区间长度的总和。
第一个while是用来比较大小塞数字用的,比较好理解。每塞一个,one/two就移一格,下面两个while就是当一组塞完以后,另一组剩下的直接塞进数组里,也比较好理解。塞完临时数组后,再把临时数组的值赋回原数组。一直递归下去就排好啦。
主函数部分也就是简单的输入输出,跟快排一样的。
#int main(){
int c,b;//b长度
printf("输入长度\n");
scanf("%d",&b);
int a[b];
for(c=0;c<b;c++){
scanf("%d",&a[c]);//赋值数组
}
f(a,0,b-1);//开始
for(c=0;c<b;c++){
printf("%d ",a[c]);//输出
}
return 0;
}
第一次写博客,有什么建议或者错误直接评论吧。 2019.10.8