冒泡排序是一种比较排序,这一节我们来看看另一种比较排序---选择排序。选择排序是对冒泡排序的一种改进。实际上冒泡排序和选择排序的核心思想都是通过轮询找出最大的元素(或者最小的元素)放到数组的最后端(或者最前端),它们的区别主要体现在最值元素放到数组最后端(或者最前端)的方法。冒泡排序是通过比较相邻两个元素的大小,然后不停交换位置,最后将最值交换到数组最后端(或者最前端)的位置;而选择排序是通过轮询标记最值位置,在此过程中其他数据位置并不改变,只是在最后将最值与需要放的位置交换,一次轮询只有一次数据交换。
算法原理
从数组中找出最小的元素,然后和第一个元素交换位置;
从第二个开始到最后,找出最小的元素,和第二个元素交换位置;
从第三个开始到最后,找出最小的元素,和第三个元素交换位置;
......
无序组元素减少,有序组元素增加,直到所有元素排好序。
算法分析
下面还是以数列 a[] = 为例,用直观的图像来演示选择排序过程。
分析:从上图可以看出选择排序过程中,可以分为有序组(蓝色标记)和无序组。每次从无序组找出最小元素,然后标记下标,最后与无序组第一个元素交换位置,直到所有元素变为有序。
第一轮有序组元素个数为0,先将最小元素指针指向 a[0] = 8 ,第1次比较 a[1] 与a[0] ,a[0] > a[1] ,将指针指向 a[1];第2次比较 a[2]与a[1], a[1] a[5],指针指向 a[5];第6次比较 a[6]与a[5], a[5]
第二轮有序组元素个数为1,先将最小元素指针指向 a[1] = 3,...
第三轮有序组元素个数为2,先将最小元素指针指向 a[2] = 7,...
第四轮有序组元素个数为3,先将最小元素指针指向 a[3] = 9,...
第五轮有序组元素个数为4,先将最小元素指针指向 a[4] = 9,...
第六轮有序组元素个数为5,先将最小元素指针指向 a[5] = 8,...
算法实现
选择排序也是不停的比较,然后找出最小元素,但是每次内循环只需要一次元素交换位置。
static void swap(int *a, int *b)
{
int val = 0;
val = *a;
*a = *b;
*b = val;
}
// 选择排序
void selection_sort(int *arr, int n)
{
int i = 0, j = 0;
int len = n;
int min = 0;
for(i=0; i
{
min = i;
for(j=i+1; j
{
if(arr[j]
min = j;
}
swap(&arr[i], &arr[min]);
}
}
算法改进
选择排序的一种优化是,每次循环同时找出最大值和最小值进行排序,个人认为这种优化效果不是特别明显,这里不作过多分析。
选择排序从 a[0] ~ a[n-1] 中选出最小元素,需要进行 n-1 次比较,然后从 a[1]~a[n-1] 中选出最小元素,又需要进行 n-2 次比较,实际我们在后面的比较中有些比较是不需要重复操作的,只是因为我们前一趟排序没有记录这些比较结果。堆排序可以通过树形结构保存部分比较结果,减少比较次数,后面我们将用单独的一节来分析堆排序。
算法性能
时间复杂度
选择排序的平均时间复杂度为o(n2)。
空间复杂度
选择排序的空间复杂度为o(1)。因为只需要为两个元素交换位置时开辟一个空间,因此空间复杂度为o(1)。
稳定性
选择排序是一种不稳定的排序算法。直接举一个例子来看看选择排序的稳定性,a[] = {3,2,3,1,4,5},第一轮循环找出最小元素 a[3] = 1,与第一个元素a[0] =3交换位置,此时得到数组a[] = ,可以看到最初红色的3在蓝色的3之前,选择排序后,红色的3却在蓝色的3之后,原数组中的两个3的相对前后顺序就被改变了,因此选择排序是一个不稳定的排序算法。
github源码连接:https://github.com/yqwung/sort
领取专属 10元无门槛券
私享最新 技术干货