这三种排序方法都需要两层循环,故时间复杂度都是O(n)
都默认升序
插入排序
核心思想
数组被分为前后两段,前一段已排序,后一段未排序。
每次取后一段的第一个数,往前查找合适的位置并插入。
void InsertSort(int arr[], size_t n) {
int key, idx; // idx指向插入的目标位置
for (int i = 1; i < n; i++) { // 第0个元素开始时默认已排好序,故从1开始
key = arr[i];
for (int j = i - 1; j >= 0; --j) {
if (arr[j] > key) { // idx指向当前判断的元素,后移更大的
arr[j + 1] = arr[j];
idx = j; // idx用来避免key为最小的情况
} else { // 找到已排序段不大于key的最大元素
idx = j + 1; // 在其后一个位置插入key
break; // 确定插入点,提前退出
}
}
arr[idx] = key;
}
}
冒泡排序
核心思想
外循环——每次冒泡出后半段未排序数组的最小的元素。
内循环——从后往前,每次比较相邻两个元素,冒泡较小的元素。
两层循环,外层需遍历n -1次,内层遍历次数依次减少。
优化:若某轮没有元素冒泡,则表示已排好序,可提前结束
void BubbleSort(int arr[], size_t n) {
int tmp;
bool exchanged;
for (int i = 0; i < n; ++i) {
exchanged = false;
for (int j = n - 1; j > i; --j) { // 从后往前
if (arr[j] < arr[j - 1]) { // 每次比较相邻两个元素,冒泡较小的
tmp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = tmp;
exchanged = true;
}
}
if (!exchanged) // 若本轮没有触发冒泡,说明排序已完成
break;
}
}
选择排序
核心思想
外循环——遍历所有元素
内循环——定位第i大的元素
void SelectSort(int arr[], size_t n) {
int idx, tmp;
for (int i = 0; i < n - 1; ++i) { // 排好前n-1个元素后,最后一个元素即为最小/大
idx = i;
for (int j = i + 1; j < n; ++j) {
if (arr[j] < arr[idx]) // 使idx指向未排序段最小的元素
idx = j;
}
if (idx != i) { // 否则,表示i指向的元素就是剩下的最小元素
tmp = arr[i];
arr[i] = arr[idx];
arr[idx] = tmp;
}
}
}