肝排序的一天

被会长要求学快排和归并,基础太差,上网看别人的代码又看不懂。不能自强就只能坐金团人带呗。找本本学了一下午的排序。

先补了前置知识——数组。

数组

就是基本的定义数组,给数组赋值,输出数组的值

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值