书接前文,师弟说道他喜欢的一个女生,让他实现一个算法,对班上的学生成绩进行排序。排序,这不是一件很简单的事情么?为什么这么说呢?因为她并没有对时间复杂度有要求。如果对时间复杂度,有要求,那就要用更高深一点的算法了。现在我们可以用最简单的2种排序算法。
选择排序
选择排序是一种简单直观的排序算法。它的工作原理是不断地选择剩余元素之中的最小或最大者,将其放置在已排序序列的末尾。
选择排序的算法步骤如下:(以从小到大排序说明,从大到小原理一样)
- 从数组未排序的部分中找到最小(或最大)元素。
- 将其存放到数组的起始位置作为已排序序列的末尾。
- 从剩余未排序元素中继续寻找最小(或最大)元素,然后放到已排序序列的末尾。
- 重复第3步,直到所有元素均排序完毕。
选择排序的时间复杂度为 O(n^2),其中 n 是数组长度。这是因为每次选出最小元素都要遍历比较所有未排序的元素。
示例说明:
原始数据: 5 3 2 4 1
一轮选择: 从0号位置开始找到最小的,1 3 2 4 5
二轮选择: 从1号位置开始找到最小的,1 2 3 4 5
三轮选择: 从2号位置开始找到最小的,1 2 3 4 5
四轮选择: 从3号位置开始找到最小的,1 2 3 4 5 //结束排序
void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void select_sort(int arr[], int n)
{
int i, j, min_idx;
for (i=0; i<n-1; i++) { // 移动边界未排序子数组
min_idx = i; // 未排序子数组中最小元素的索引
for (j=i+1; j<n; j++) {
if (arr[j] < arr[min_idx]) {
min_idx = j;
}
}
// 将找到的最小元素与第一个未排序元素进行交换
swap(&arr[min_idx], &arr[i]);
// 打印排序的详细过程
int k;
for (k=0; k<n; k++) {
printf("%d ", arr[k]);
}
printf("\n");
}
}
冒泡排序
冒泡排序是一种简单的排序算法,它重复地遍历要排序的列表,比较相邻的两个项,如果相邻不是预期的顺序时交换它们。这个算法的名称由来是因为较小的元素会像"气泡"一样逐渐"浮"到列表的顶端。
下面是冒泡排序算法的步骤:(以从小到大排序说明,从大到小原理一样)
- 比较相邻的元素。如果第一个比第二个大,就交换它们两个。
- 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。做完这步后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
冒泡排序的时间复杂度是 O(n^2),其中 n 是列表中元素的数量。
示例说明:
原始数据: 5 3 2 4 1
一轮冒泡: 5>3,冒泡,3 5 2 4 1; 5>2,冒泡,3 2 5 4 1;5>4,冒泡,3 2 4 5 1;5>1,冒泡,3 2 4 1 5 //一轮找出最大值
二轮冒泡: 2 3 1 4 5 //二轮找出第二大值
三轮冒泡: 2 1 3 4 5 //三轮找出第三大值
四轮冒泡: 1 2 3 4 5 //四轮完成排序
void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void bubble_sort(int arr[], int n)
{
int i, j;
for (i = 0; i < n-1; i++) {
for (j = 0; j < n-i-1; j++) { // 最后 i 个已经排序好了, 遍历未排序的部分
if (arr[j] > arr[j+1]) {
// 如果当前元素大于后面的元素,交换它们
swap(&arr[j], &arr[j+1]);
}
}
// 打印排序的详细过程
int k;
for (k=0; k<n; k++) {
printf("%d ", arr[k]);
}
printf("\n");
}
}
师弟有如获得绝世秘籍一般,回去吭哧吭哧,写了起来,沙漠中前行,远处的白杨树,显得那么的小,却也逐渐清晰的样子。
这里介绍的只是入门级排序,如果只是对于少量数据排序,且又对时间消耗没有严格的要求,是可以使用的。如果对时间有严格要求,则需要更优的排序,如快速排序、堆排序、归并排序等。