选择排序
背景
选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n²) 的时间复杂度。所以用到它的时候,数据规模越小越好。
算法思想
选择排序的算法思想非常朴素:从前往后,每次从还未被排序的序列中选择当前位置需要放置的元素,存放在这一次的起始位置,直到序列中的所有元素都被选择过为止。
用伪代码来表示即为:
for i = 1 .. A.length - 1
minimum = i // 初始化最小值的位置
for j = i .. A.length
if A[j] < A[minimum]
minimum = j // 如果A[j]值更小,更新minimum
swap(A[minimum], A[i])
算法的具体执行过程如下所示:
代码实现
算法思想十分朴素,代码实现起来跟伪代码的形式几乎一致
#include <stdio.h>
#define ARR_SIZE(arr) (sizeof(arr) / sizeof(arr[ 0 ])) // 获取数组的元素个数
/** void print_vec(int arr[], int n) -> none
* 选择排序辅助打印函数,用于格式化打印
* @params
* @param arr: 需要打印的数组
* @param n: 数组的元素个数
* @return: none
*/
static inline void print_vec(int arr[], int n) {
for (int i = 0; i < n; i++) {
printf("%-3d ", arr[ i ]);
}
printf("\n");
}
/** void swap(int *a, int *b) -> none
* 选择排序辅助交换函数,用于交换数组中的两个元素
* @params
* @param a: 需要交换的第一个元素的地址
* @param b: 需要交换的第二个元素的地址
* @return: none
*/
static inline void swap(int *a, int *b) {
*a = *a ^ *b;
*b = *a ^ *b;
*a = *a ^ *b;
}
/** void select_sort(int arr[], int n) -> none
* 选择排序函数主体
* @params
* @param arr: 需要排序的数组
* @param n: 数组的元素个数
* @return: none
*/
void select_sort(int arr[], int n) {
int minimum;
for (int i = 0; i < n - 1; i++) {
minimum = i;
for (int j = i + 1; j < n; j++) {
if (arr[ j ] < arr[ minimum ]) {
minimum = j;
}
}
if (minimum ^ i) {
swap(&arr[ i ], &arr[ minimum ]);
}
}
}
int main() {
int arr[] = {82, 10, 65, 63, 523, 254, 423, 74};
int n = ARR_SIZE(arr);
print_vec(arr, n);
select_sort(arr, n);
print_vec(arr, n);
return 0;
}
算法分析
时间复杂度
选择排序的交换操作介于 0 0 0和 ( n − 1 ) (n-1) (n−1)次(完全逆序)之间,需要进行的比较操作为 n ( n − 1 ) / 2 n(n-1)/2 n(n−1)/2。综上,简单排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
空间复杂度
简单选择排序需要占用1个临时空间,用于保存最小值得索引,因此空间复杂度为 O ( 1 ) O(1) O(1)。不需要额外的空间几乎是选择排序的唯一优点,当空间复杂度要求较高时,可以考虑选择排序,但是实际适用的场合非常罕见,在需要排序的元素规模较小的时候,可以使用选择排序。
算法稳定性
选择排序每次只会选择一个元素放在指定的位置,因此算法是稳定的。
要求较高时,可以考虑选择排序,但是实际适用的场合非常罕见,在需要排序的元素规模较小的时候,可以使用选择排序。
算法稳定性
选择排序每次只会选择一个元素放在指定的位置,因此算法是稳定的。