数据结构——冒泡排序、选择排序(C语言)

目录

1.排序

2.冒泡排序(交换排序类)

3.冒泡排序的优化

4.选择排序(选择排序类)

 5.选择排序的优化


1.排序

简单来说,排序就是将一些数据元素按照一定的顺序进行整理排序,使得原本无序的数据变得有序例如,一个班里的一次考试中,每个人的各科成绩和总成绩都可能会有所不同,通过排序我们将这些成绩数据按照从大到小的顺序进行排序,这样就可以知道每个人的排名,对于总分一样的可以通过依次比较语文、数学、英语...成绩的大小来确定排名顺序。我们可以通过编程来进行解决排序问题。

在排序问题中,通常将数据元素称为记录。显然我们输入的是一个记录集合,排序后输出的也是一个记录集合,所以我们也可以将排序看作是线性表的一种操作。排序的依据是关键字之间的大小关系,那么对同一个记录集合,针对不同的关键字进行排序,可以得到不同序列。例如对一个班里的学生成绩,只看某一科的成绩来确定单科排名,或者根据学科比重来确定最终排名。


2.冒泡排序(交换排序类)

冒泡排序的基本思想两两相邻记录的关键字,如果反序则交换直到没有反序的记录为止。

1.“两两”是相邻的两个元素的意思

2.如果有n个元素需要比较n-1次,每一轮减少1次比较

3.顾名思义,冒泡就是从下往上两两比较,小的元素会向上冒,既交换位置

如下图是一趟比较:

 元素1就像冒泡一样通过交换实现向上移动

实现冒泡排序的代码如下:

#include<stdio.h>
void maopao(int a[],int n)
{
	int t;	//t作为交换相邻数组的值时所需的中间变量
	for(int i=0;i<n-1;i++)	//n个元素要进行n-1趟比较
	{
		for(int j=n-1;j>i;j--)	//从后往前遍历
		{
			if(a[j-1]>a[j])		//如果元素比它前面的元素小,则交换两者的位置
			{
				t=a[j-1];
				a[j-1]=a[j];
				a[j]=t;
			}
		}
	}
}
int main()
{
	int a[10]={4,8,9,3,5,1,6,7,2,0};	//初始化数组
	maopao(a,10);
	printf("排序后的数组为:\n");
	for(int k=0;k<=9;k++)
	{
		printf("%d ",a[k]);	//输出排序后的数组
	}
	return 0;
}

3.冒泡排序的优化

上面的冒泡排序程序对于某些特殊情况会显得有些多此一举例如对于一个本身已经排好序的有n个数据的数组再继续进行冒泡排序的话,该程序依然会进行n-1轮比较,但是并不交换元素,即没有对数组做任何改变,又或是对一个有n个数据的数组进行排序时,当进行小于n-1趟比较后数组已经是有序的了,但是上面的程序依然会进行比较,直到第n-1轮比较完成,这显然是不划算的,浪费了很多时间。

针对上述情况,为了提高程序的效率,我们额外增加一个标志变量flog,用来标志进行比较时有无交换操作,如果没有交换操作,也就是数组已经排成有序了,不需要再进行循环了,可以提前结束循环。具体代码如下:

#include<stdio.h>
void maopao(int a[],int n)
{
	int t,flog=1;	//定义标志变量flog
	for(int i=0;i<n-1 && flog;i++)	//循环条件加上标志变量不为0
	{
		for(int j=n-1;j>i;j--)	
		{
			flog=0;		//将flog变为0,如果执行了交换元素的操作,会重新变为1
			if(a[j-1]>a[j])		
			{
				t=a[j-1];
				a[j-1]=a[j];
				a[j]=t;
				flog=1;		//执行了交换操作,重新赋值为1
			}
		}
	}
}
int main()
{
	int a[10]={5,2,6,0,3,9,1,7,4,8};	
	maopao(a,10);
	printf("排序后的数组为:\n");
	for(int k=0;k<=9;k++)
	{
		printf("%d ",a[k]);	//输出排序后的数组
	}
	return 0;
}

4.选择排序(选择排序类)

选择排序算法就是通过n-i次关键字的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1<=i<=n)个记录交换。即从头到尾扫描序列,找出最小的一个元素,和第一个元素交换,接着从剩下的元素中继续这种选择和交换方式,最终得到一个有序序列。 

下图是对有4个元素的数组进行选择排序:

 具体的实现代码如下:

#include<stdio.h>
void xuanze(int a[],int n)
{
	int t,min,b;	//t为下面交换值时需要用到的中间变量,min用来存放每一趟中最小的数,b用来存放每一趟中最小数对应的下标
	for(int i=0;i<n-1;i++)	//对于n个数的数组只需要进行n-1次比较
	{
		min=a[i];	//每一趟开始都假设最左边的是最小的,方便后面进行交换
		for(int j=i+1;j<=n;j++)	//j每次从i的后一个开始,方便比较
		{
			if(min>a[j])	//寻找最小的数
			{
				min=a[j];	//用min存放最小数
				b=j;		//用b存放最小数对应的下标
			}
		}
		if(min!=a[i])	//当最小数不是它本身时,才进行交换
		{
			t=a[i];
			a[i]=min;
			a[b]=t;
		}
	}
}

int main()
{
	int a[10]={3,2,7,9,0,4,1,5,6,8};
	xuanze(a,10);
	printf("排序后的结果为:\n");
	for(int k=0;k<10;k++)
	{
		printf("%d ",a[k]);
	}
}

主要注意变量min和b的使用。通过选择排序可以减少交换操作的次数,每次都统计出最小的数和其对应的下标在进行交换,每次都出一次手,但是一出手就能排好一个元素


 5.选择排序的优化

上面的选择排序是对n个元素的数组进行n-1趟比较交换,每次都只选取出最小值,然后将最小的元素排在最左边,即一次完成一个元素的排序,针对这一现象,我们可以对其进行优化。而优化的具体思路就是,从每次只选出一个最小值,完成一个元素的排序变成每次都选出一个最小值和一个最大值,把最小的放在左边,最大的放在右边,每次完成两个元素的排序。优化后的选择排序所进行的循环趟数将减半,效率提高

具体的实现代码如下:

#include<stdio.h>
void xuanze(int a[],int n)
{
	int i,left=0,right=n-1,t,min,max;	//一开始left、right分别0、n-1,然后往中间靠拢,min、max分别存储每一趟的最小值和最大值的下标,t为交换数据时所用到的中间变量
	while(left<right)	//循环条件为每次循环的左边不等于右边
	{
		max=left;
		min=left;
		for(i=left;i<=right;i++)	
		{
			if(a[i]<a[min])		//如果找到更小的,用min记录其下标
			{
				min=i;
			}
			if(a[i]>a[max])		//如果找到更大的,用max记录其下标
			{
				max=i;
			}
		}
		if(min!=left)		//如果最小的不是它自身,则交换位置
		{
			t=a[min];
			a[min]=a[left];
			a[left]=t;
		}
		if(max==left)		//如果最大的数在最左边,因为上一步操作已经把max交换为min,若要实现目的,应该交换回来
		{
			max=min;
		}
		if(max!=right)		//如果最大的不是它自身,则交换位置
		{
			t=a[max];
			a[max]=a[right];
			a[right]=t;
		}
		left++;		//left和right逐渐往中间靠
		right--;
	}
}

int main()
{
	int a[10]={3,1,9,2,5,8,7,6,0,4};
	xuanze(a,10);
	printf("排序后的结果为:\n");
	for(int k=0;k<10;k++)
	{
		printf("%d ",a[k]);
	}
	return 0;
}

其中需要注意的是,对特殊情况:“最大的数在本趟的最左边”的处理。

 


后续会继续分享其他排序算法,敬请期待~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值