排序算法一之冒泡 选择 插入 希尔排序

   本文中的前三种排序都是升序排序,又因为这三个排序的平均时间复杂度都是O(n2),最差情况也都是O(n2),另外这三种排序也是其他排序如快排、希尔排序的基础,代码较少,思想较容易理解,所以放在一块。

   对于排序的思想可以参考严蔚敏的《数据结构》的书,也可以参考《大话数据结构》,或者另外一本《妙趣横生的算法(c语言实现)》,最后一本书只推荐前三章,后面的章节有点偏数学的应用

  另外感觉希尔排序是基于冒泡排序的,代码和思想相对堆排序、快排、归并都简单一些,所以补充进来


冒泡排序

   就是交换相邻的两个数,每次排完序之后都会出现一个最大数,第二次排序就是在前n-1个数排序,第i次排序就是前n-i个数排序

代码如下:

void swap(int *s,int *t){
	int temp=*s;
	*s=*t;
	*t=temp;
}

void bubbleSort(int a[],int n){
	int i,j;
	for(i=0;i<n;i++){
		for(j=0;j<n-i-1;j++){
			if(a[j]>a[j+1])
				swap(&a[j],&a[j+1]);
		}
	}
}

对于如此简单的一个冒泡的程序,我在写的时候也是出了一些问题,关键就是第二个for循环怎么写的问题:

1.我们知道最外层的排序的次数肯定是n次,内层for循环的次数j<?,我们知道每次都会出现一个最大数,所以循环次数肯定有n-i,但是是不是就是n-i?,假设最简单的n=1,那么内层不应该循环,因为只有一个数,如果是n-i,那么就是i=0;i<1,就需要循环一次,明显不对,此时应该是i=0,j<1-0-1,也就是j<n-i-1,另外,我们从理论的角度来分析,每次循环都会产生(i+1)个有序排列,比如一次之后,有一个最大数,有1个数有序,所以前面只有n-(i+1)个数为排列,所以就是n-i-1

2.其实初始条件我先开始写成j=i了,但是仔细一想,每次都是从头重新比较

有了这两点,相信冒泡排序就不会写错了


改进版的冒泡排序,原理就是当所有的比较之后,发现都不需要交换时,此时肯定已经有序

代码如下:

void bubbleSort2(int a[],int n){
	int i,j;
	int flag;
	for(i=0;i<n;i++){
		flag=0;
		for(j=0;j<n-i-1;j++){
			if(a[j]>a[j+1]){
				flag=1;
				swap(&a[j],&a[j+1]);
			}
		}
		if(flag==0) break;
	}
}


选择排序

   就是每次都选择当前无序数组中的一个最大数的与数组的最后一个数交换,我相信写过了求一个数组中的最大数之后,再写选择排序就迎刃而解

void selectionSort(int a[],int n){
	int i,j;
	for(i=0;i<n;i++){
		int max=a[0],k=0;
		for(j=0;j<n-i;j++){
			if(a[j]>max){
				 max=a[j];
				 k=j;
			}
		}
		if(j-1!=k)
			swap(&a[j-1],&a[k]);
	}
}
写选择排序的时候,没有碰到什么问题,就是max的时候,同时需要一个下标变量,记住这个最大数所在的位置,这样交换的时候才知道是哪两个数交换,但是第二个for循环和冒泡的不同,因为当n=1时,内层总是需要找出最大的那个值,需要循环一次,所以推导是n-i,最开始写的时候忽略了第二个if条件,后来补上


插入排序

   这个用的比较少,可能是代码看得少,一开始都不知道怎么排,当时想通了原理,也就没啥了,所谓插入,就是前面已经有序,最后插入一个数,把这个数放在他应该放的位置就完了,比如 1 2 4 5,现在来了一个数字a[i=4]=3,现在需要思考的问题就是如何把3放在正确的位置也就是2和4之间,首先就是大于3的数全都往后移一位,直至某个数小于3,那么当前位置就应该是3所在的位置

代码如下

void insertSort(int a[],int n){
	int i,j;
	for(i=1;i<n-1;i++){
		int temp=a[i];
		j=i-1;
		while(a[j]>temp){
			a[j+1]=a[j];
			j--;
		}
		a[j+1]=temp;
	}
}

   但是插入排序需要注意的细节比较多,首先j=i-1;而不是j=i,因为j要表示之前已将排序的数组的最大下标,还有就是while循环完了之后,是a[j+1]=temp,而不是a[j]=temp,因为a[1]=2<3,所以j此时等于1,而3应该是等于a[2]才对,所以j+1


希尔排序

   就是相当于有一个步长或者跳数,讲这些相隔k个数相交换,可能有10个数,第一次步长是5,那么就是a[0]和a[5],a[1]和a[6]。。。,一趟排序之后,改变步长为3,然后a[0],a[3],a[6],a[9]相交换,。。。最后直至步长为1,这里采取的是冒泡版的希尔排序

代码如下:

void shellSort(int a[],int n){
	int i,k=n;
	while(k){
		k/=2;
		for(i=0;i<n-k;i++){
			if(a[i]>a[i+k]) swap(&a[i],&a[i+k]);
		}
	}
}
   希尔排序的代码量比不大,可能和改进的冒泡的代码来那个差不多,一个多了一个flag,希尔排序是设置了一个步长,感觉理解了思想,代码没啥问题


最后总的代码如下(主函数修改了随机数的生成,原来没有利用srand(seed),每次生成的随机数都是一样)

#include <stdio.h>
#include <stdlib.h>
#define MAXN 10

void swap(int *s,int *t){
	int temp=*s;
	*s=*t;
	*t=temp;
}

void bubbleSort(int a[],int n){
	int i,j;
	int flag;
	for(i=0;i<n;i++){
		for(j=0;j<n-i-1;j++){
			if(a[j]>a[j+1]){
				swap(&a[j],&a[j+1]);
			}
		}
	}
}

void bubbleSort2(int a[],int n){
	int i,j;
	int flag;
	for(i=0;i<n;i++){
		flag=0;
		for(j=0;j<n-i-1;j++){
			if(a[j]>a[j+1]){
				flag=1;
				swap(&a[j],&a[j+1]);
			}
		}
		if(flag==0) break;
	}
}

void selectionSort(int a[],int n){
	int i,j;
	for(i=0;i<n;i++){
		int max=a[0],k=0;
		for(j=0;j<n-i;j++){
			if(a[j]>max){
				 max=a[j];
				 k=j;
			}
		}
		if(j-1!=k)
			swap(&a[j-1],&a[k]);
	}
}

void insertSort(int a[],int n){
	int i,j;
	for(i=1;i<n-1;i++){
		int temp=a[i];
		j=i-1;
		while(a[j]>temp){
			a[j+1]=a[j];
			j--;
		}
		a[j+1]=temp;
	}
}

void shellSort(int a[],int n){
	int i,k=n;
	while(k){
		k/=2;
		for(i=0;i<n-k;i++){
			if(a[i]>a[i+k]) swap(&a[i],&a[i+k]);
		}
	}
}
 
void display(int a[],int n){
	int i;
	for(i=0;i<n;i++){
		printf("%d ",a[i]);
	}
	printf("\n");
}
 
int main(int argc, char *argv[]) {
	unsigned int seed;
     seed = time(0);
     srand(seed);
	int i,a[MAXN];
	for(i=0;i<MAXN;i++){
		a[i]=rand()%100+1;
	}
	bubbleSort2(a,MAXN);
	display(a,MAXN);
	bubbleSort(a,MAXN);
	display(a,MAXN);
	selectionSort(a,MAXN);
	display(a,MAXN);
	insertSort(a,MAXN);
	display(a,MAXN); 
	shellSort(a,MAXN);
	display(a,MAXN); 
	return 0;
}


未完待续,还有快排、堆排序、归并排序

如需参考快排,归并排序,请链接:排序算法二之快速 归并排序


如果文章有什么错误或者有什么建议,欢迎提出,大家共同交流,一起进步

文章转载请注明出处,请尊重知识产权


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值