概念:
选择排序是一种简单直观的排序算法,其基本思想是在每一轮选择中找到未排序部分的最小(或最大)元素,然后将其与未排序部分的第一个元素交换位置。选择排序无论最好、最坏和平均情况下的时间复杂度都是O(n^2),其中n是数组的长度。尽管选择排序不是最高效的排序算法,但它的优点是算法简单,且在数据量较小或部分数据已经有序的情况下,性能表现尚可。
算法步骤:
以下是选择排序的基本步骤:
-
找到最小元素:在未排序的数组部分,找到最小元素的索引。
-
交换元素:将找到的最小元素与未排序部分的第一个元素交换位置。
-
缩小范围:将未排序部分的范围缩小,排除已经排序好的元素。
-
重复选择:重复步骤1到3,直到整个数组都被排序。
我们通过例子来对选择排序进行解释(这里我们取少点的数字集合(比较好解释),当然,大的数字集合也一样)(也是默认从小到大)
EG :2 3 1 5
从第一个数开始
用第一个数与后面三个数进行比较
第一个数是2:
2与3比较,2<3,不交换;
2与1比较,1<2,交换;
交换后,第一个数为1,第三个数为2
1与5比较,1<5,不交换;
这么一轮下来,最小的数排放到了第一位;
1 3 2 5
以此类推:
3与后面的2,5比较
又这么一轮下来,第二小的数排放到了第二位;
1 2 3 5
..............
通过4-1轮(n-1)轮,就排序好了
OK,接下来,我们开始以选择排序的法子来写代码
注意:选择排序用的是双层循环
外层循环:用来表明我们要排多少次,是我们排序的次数:sz-1
内层循环:我们要比较的后面的数据
代码实现:
#include <iostream>
#include <vector>
#include <algorithm> // 引入algorithm库以使用std::swap
using namespace std;
// 选择排序函数
void selectionSort(vector<int>& arr) {
int n = arr.size();
for (int i = 0; i < n - 1; i++) {
// 找到未排序部分的最小元素的索引
int minIndex = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// 将找到的最小元素交换到未排序部分的起始位置
swap(arr[minIndex], arr[i]);
}
}
效果展示:
#include <iostream>
#include <vector>
#include <algorithm> // 引入algorithm库以使用std::swap
using namespace std;
// 选择排序函数
void selectionSort(vector<int>& arr) {
int n = arr.size();
for (int i = 0; i < n - 1; i++) {
// 找到未排序部分的最小元素的索引
int minIndex = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// 将找到的最小元素交换到未排序部分的起始位置
swap(arr[minIndex], arr[i]);
}
}
Original array: 64 25 12 22 11
Sorted array: 11 12 22 25 64
D:\2024C语言\data-structure\SelectionSort\x64\Debug\SelectionSort.exe (进程 9308)已退出,代码为 0 (0x0)。
按任意键关闭此窗口. . .
selectionSort
函数实现了选择排序算法。它通过两层循环来遍历数组,外层循环负责未排序部分的起始位置,内层循环负责在未排序部分找到最小元素的索引。然后使用swap
函数将找到的最小元素交换到未排序部分的起始位置。printArray
函数用于打印数组,让我们可以看到排序前后的对比。main
函数中定义了一个未排序的数组,并调用selectionSort
函数对其进行排序。
尽管选择排序的性能不是最优的,但它的不稳定性(相同元素可能会因为排序而改变顺序)和易于实现的特点使其在某些情况下仍然有其应用场景。
优化:
选择排序算法由于其固有的设计,其时间复杂度在最坏、最好和平均情况下都是O(n^2),这意味着它在处理大数据集时效率较低。然而,尽管不能从根本上改变其时间复杂度,我们仍然可以采取一些措施来优化选择排序算法,提高其在特定情况下的效率:
-
减少交换次数:在每次遍历中,如果发现最小元素已经是当前未排序部分的第一个元素,那么就不需要进行交换。
-
双向选择排序(也称为鸡尾酒排序):这种变体在每一轮中同时找到未排序部分的最小和最大元素,并分别将它们移动到正确的位置。这样可以减少遍历次数。
-
小数组优化:对于小数组,选择排序的性能可能与其他更复杂的排序算法相当,甚至更好。因此,如果预计数据集的大小较小,或者在排序前可以快速判断数据集的大小,选择排序可能是一个不错的选择。
-
并行化:在某些情况下,选择排序的最小元素查找部分可以并行化。例如,如果数据集分布在多个处理器上,每个处理器可以独立地找到其数据子集的最小元素。
-
优化数据移动:在找到最小元素后,如果它不在当前位置,可以一次性将所有元素移动到它们最终的位置,而不是仅仅交换最小元素。
-
使用更高效的算法:如果可能的话,考虑使用更高效的排序算法,如快速排序、归并排序或堆排序,这些算法在大多数情况下的性能都优于选择排序。
以下是双向选择排序(鸡尾酒排序)的C++代码:
#include <iostream>
#include <vector>
#include <algorithm> // 引入algorithm库以使用std::swap
using namespace std;
// 双向选择排序(鸡尾酒排序)
void cocktailSort(vector<int>& arr) {
int n = arr.size();
bool swapped = true;
int start = 0, end = n - 1;
while (swapped) {
swapped = false;
// 向左扫描,找到最小元素
for (int i = start; i < end; i++) {
if (arr[i] > arr[i + 1]) {
swap(arr[i], arr[i + 1]);
swapped = true;
}
}
// 如果没有发生交换,数组已经排序完成
if (!swapped)
break;
end--;
// 向右扫描,找到最大元素
for (int i = end; i > start; i--) {
if (arr[i] < arr[i - 1]) {
swap(arr[i], arr[i - 1]);
swapped = true;
}
}
start++;
}
}
// 打印数组的函数
void printArray(const vector<int>& arr) {
for (int num : arr) {
cout << num << " ";
}
cout << endl;
}
// 主函数
int main() {
vector<int> arr = {64, 34, 25, 12, 22, 11, 90};
cout << "Original array: ";
printArray(arr);
cocktailSort(arr);
cout << "Sorted array: ";
printArray(arr);
return 0;
}
在这个示例中,cocktailSort
函数实现了双向选择排序,它可以在每一轮中同时找到最小和最大元素,并将它们移动到数组的两端。这种方法可以减少遍历次数,提高排序效率。然而,即使如此,鸡尾酒排序的时间复杂度仍然是O(n^2)。