六大排序:冒泡、插入、选择、快速、归并、希尔
排序算法
冒泡排序
/*
冒泡排序
从数组最左端开始向右遍历,依次比较相邻元素大小,如果“左元素 > 右元素”就交换二者。
遍历完成后,最大的元素会被移动到数组的最右端。
1.首先,对n个元素执行“冒泡”,将数组的最大元素交换至正确位置。
2.接下来,对剩余n-1个元素执行“冒泡”,将第二大元素交换至正确位置
3.以此类推,经过n-1轮“冒泡”后,前n-1大的元素都被交换至正确位置。
4.仅剩的一个元素必定是最小元素,无须排序,因此数组排序完成
*/
void bubbleSort(std::vector<int>& arr) {
int n = arr.size();
for (int i = 0; i < n - 1; ++i) {
bool is_swap = false;
for (int j = 0; j < n - 1 - i; ++j) {
if (arr[j] > arr[j + 1]) {
std::swap(arr[j], arr[j + 1]);
is_swap = true;
}
}
if (!is_swap) break;
}
}
插入排序
/*
插入排序
在未排序区间选择一个基准元素,
将该元素与其左侧已排序区间的元素逐一比较大小,并将该元素插入到正确的位置。
1.初始状态下,数组的第 1 个元素已完成排序。
2.选取数组的第 2 个元素作为 base ,将其插入到正确位置后,数组的前 2 个元素已排序。
3.选取第 3 个元素作为 base ,将其插入到正确位置后,数组的前 3 个元素已排序。
4.以此类推,在最后一轮中,选取最后一个元素作为 base ,将其插入到正确位置后,所有元素均已排序。
*/
void insertionSort(std::vector<int>& arr) {
int n = arr.size();
for(int i = 1; i < n; ++i) {
for (int j = i; j > 0 && arr[j] < arr[j - 1]; --j) {
std::swap(arr[j], arr[j - 1]);
}
}
}
选择排序
/*
选择排序
开启一个循环,每轮从未排序区间选择最小的元素,将其放到已排序区间的末尾。
1.初始状态下,所有元素未排序,即未排序(索引)区间为[0,n-1].
2.选取区间[0,n-1]中的最小元素,将其与索引0处的元素交换。完成后,数组前 1 个元素已排序。
3.选取区间[1,n-1]中的最小元素,将其与索引1处的元素交换。完成后,数组前 2 个元素已排序。
4.以此类推。经过n-1轮选择与交换后,数组前n-1个元素已排序。
5.仅剩的一个元素必定是最大元素,无须排序,因此数组排序完成。
*/
void selectionSort(std::vector<int>& arr) {
int n = arr.size();
for (int i = 0; i < n - 1; ++i) {
int mid = i;
for (int j = i + 1; j < n; ++j) {
if (arr[j] < arr[mid]) {
mid = j;
}
}
std::swap(arr[i], arr[mid]);
}
}
快速排序
/* 哨兵划分 */
int partition(vector<int> &nums, int left, int right) {
// 以 nums[left] 为基准数
int i = left, j = right;
while (i < j) {
while (i < j && nums[j] >= nums[left])
j--; // 从右向左找首个小于基准数的元素
while (i < j && nums[i] <= nums[left])
i++; // 从左向右找首个大于基准数的元素
swap(nums, i, j); // 交换这两个元素
}
swap(nums, i, left); // 将基准数交换至两子数组的分界线
return i; // 返回基准数的索引
}
// 选择数组中的某个元素作为“基准数”,
// 将所有小于基准数的元素移到其左侧,而大于基准数的元素移到其右侧。
/*
1.选取数组最左端元素作为基准数,初始化两个指针 i 和 j 分别指向数组的两端。
2.设置一个循环,在每轮中使用 i(j)分别寻找第一个比基准数大(小)的元素,然后交换这两个元素。
3.循环执行步骤 2. ,直到 i 和 j 相遇时停止,最后将基准数交换至两个子数组的分界线。
*/
/* 快速排序 */
void quickSort(vector<int> &nums, int left, int right) {
// 子数组长度为 1 时终止递归
if (left >= right)
return;
// 哨兵划分
int pivot = partition(nums, left, right);
// 递归左子数组、右子数组
quickSort(nums, left, pivot - 1);
quickSort(nums, pivot + 1, right);
}
归并排序
* 合并左子数组和右子数组 */
void merge(vector<int> &nums, int left, int mid, int right) {
// 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
// 创建一个临时数组 tmp ,用于存放合并后的结果
vector<int> tmp(right - left + 1);
// 初始化左子数组和右子数组的起始索引
int i = left, j = mid + 1, k = 0;
// 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
while (i <= mid && j <= right) {
if (nums[i] <= nums[j])
tmp[k++] = nums[i++];
else
tmp[k++] = nums[j++