目录
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;
}
其中需要注意的是,对特殊情况:“最大的数在本趟的最左边”的处理。
后续会继续分享其他排序算法,敬请期待~