选择排序
选择排序(Selection Sort)是一种简单直观的排序算法,其基本思想是每次从未排序的部分中选择最小(或最大)的元素,然后将其放到已排序部分的末尾。其操作步骤如下:
算法思想
-
工作原理:
- 首先,在未排序序列中找到最小(或最大)的元素,存放到序列的起始位置。
- 然后,再从剩余未排序元素中继续寻找最小(或最大)的元素,放到已排序序列的末尾。
- 以此类推,直到所有元素均排序完毕。
-
具体步骤:
- 第一次迭代:从数组的第一个元素开始,假设它是最小的。然后依次与后面的元素比较,找到整个数组中的最小元素,将其与第一个元素交换位置。
- 第二次迭代:从第二个元素开始,重复上述过程,找到剩余数组中的最小元素,将其与第二个元素交换位置。
- 依此类推,每次迭代都会确定一个当前未排序数组的最小元素,并将其放到已排序数组的末尾。
-
时间复杂度:
- 选择排序的时间复杂度是 (O(n^2)),其中 (n) 是数组的大小。这是因为在每次迭代中,需要找到未排序部分的最小元素,而找最小元素的操作需要 (O(n)) 的时间,总共需要进行 (n-1) 次这样的操作。
-
空间复杂度:
- 选择排序是一种原地排序算法,只需要常数级别的额外空间用于存储若干变量。
-
稳定性:
- 选择排序是一种不稳定的排序算法。例如,在排序过程中可能会交换相同元素的相对位置,导致排序后相同元素的相对顺序发生变化。
-
适用场景:
- 选择排序简单直观,适用于小数据量或者是简单实现的排序需求。但对于大规模数据或者性能要求较高的情况,选择排序通常不是首选,因为其时间复杂度较高。
完整代码
#include <iostream>
#include <vector>
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;
// 将最小元素交换到已排序部分的末尾
if (minIndex != i)
swap(arr[i], arr[minIndex]);
}
}
在这段代码中展示了如何实现和使用选择排序来对一个整数数组进行排序。选择排序的实现通过找到未排序部分的最小元素,并与当前位置的元素进行交换,直到整个数组排序完成。
冒泡排序
冒泡排序(Bubble Sort)是一种简单直观的排序算法,其基本思想是多次遍历待排序的序列,每次遍历过程中依次比较相邻的两个元素,如果它们的顺序错误(例如,升序时当前元素大于后面的元素),则交换它们的位置。这样一次遍历后,序列中最大(或最小)的元素就“浮”到了最右端。重复这个过程,每次遍历都能确定一个未排序部分的最大(或最小)元素,直到整个序列有序为止。
算法思想
-
工作原理:
- 从序列的第一个元素开始,依次比较相邻的两个元素,如果顺序错误就交换它们的位置,直到把当前序列中的最大元素交换到最后的位置。
- 接着,针对剩余的未排序序列(即除去最后一个元素的其他元素),重复以上步骤,直到所有元素都排好序为止。
-
具体步骤:
- 第一次迭代:从序列的第一个元素开始,比较相邻的两个元素,如果顺序错误则交换它们的位置。
- 第二次迭代:继续比较相邻的元素,直到把当前序列中的第二大元素交换到倒数第二个位置。
- 依此类推,每次迭代都会确定一个当前未排序序列的最大元素,直到整个序列有序。
-
时间复杂度:
- 冒泡排序的时间复杂度是 (O(n^2)),其中 (n) 是序列的大小。这是因为它需要进行 (n-1) 轮遍历,每轮遍历最多需要比较和交换 (n-i) 次,其中 (i) 是当前迭代的次数。
-
空间复杂度:
- 冒泡排序是一种原地排序算法,只需要常数级别的额外空间用于存储若干变量。
-
稳定性:
- 冒泡排序是一种稳定的排序算法。相同元素的相对位置在排序过程中不会发生改变,只有相邻元素才会进行比较和交换。
-
适用场景:
- 冒泡排序适用于数据量较小或者基本有序的情况。在实际应用中,由于其简单直观的特性,通常用于教学和理解排序算法的基本原理。
完整代码
#include <iostream>
#include <vector>
using namespace std;
void bubbleSort(vector<int>& arr)
{
int n = arr.size();
for (int i = 0; i < n - 1; ++i)
{
// 提前退出标志,如果一次遍历没有发生交换,则序列已经有序
bool swapped = false;
// 每次遍历把当前未排序部分的最大元素交换到最右端
for (int j = 0; j < n - i - 1; ++j)
{
if (arr[j] > arr[j + 1])
{
swap(arr[j], arr[j + 1]);
swapped = true;
}
}
// 如果本次遍历没有发生交换,说明序列已经有序,可以提前退出
if (swapped == false) break;
}
}
在这段代码中,我们展示了如何实现和使用冒泡排序来对一个整数数组进行排序。冒泡排序的实现通过每次遍历找到当前未排序部分的最大元素,并将其交换到正确的位置,直到整个数组排序完成。
插入排序
插入排序(Insertion Sort)是一种简单直观的排序算法,其基本思想是将一个序列分为已排序和未排序两部分,初始时已排序部分只有一个元素(即序列的第一个元素),然后逐步将未排序部分的元素插入到已排序部分的正确位置,直到整个序列有序为止。
算法思想
-
初始状态:
- 将序列的第一个元素视为已排序部分,其余元素视为未排序部分。
-
排序过程:
- 从第二个元素开始,依次将它与已排序部分的元素进行比较。
- 找到插入位置:将当前未排序的元素依次与已排序部分的元素比较,直到找到合适的位置插入,使得插入后的序列仍然保持有序。
- 插入操作:将当前元素插入到已排序部分的正确位置,插入位置之后的元素依次后移,为插入元素腾出位置。
-
重复:
- 重复上述过程,直到未排序部分的所有元素都被插入到已排序部分,整个序列变成有序状态。
-
时间复杂度:
- 插入排序的时间复杂度为 (O(n^2)),其中 (n) 是序列的长度。最坏情况下(逆序序列),每个元素都可能需要与已排序部分的所有元素比较。
-
空间复杂度:
- 插入排序是一种原地排序算法,只需要常数级别的额外空间用于存储若干变量。
-
稳定性:
- 插入排序是一种稳定的排序算法。相同元素的相对位置在排序过程中不会发生改变,只有相邻元素才会进行比较和交换。
完整代码
#include <iostream>
#include <vector>
using namespace std;
void insertionSort(vector<int>& arr)
{
int n = arr.size();
int j = 0;
for (int i = 1; i < n; ++i)
{
int temp = arr[i]; // 当前要插入的元素
// 在已排序部分找到合适的插入位置
for (j = i - 1; j >= 0; j--)
{
if (temp < arr[j])
arr[j + 1] = arr[j];// 往后移动元素
else
break;
}
arr[j + 1] = temp; // 插入元素到合适位置
}
}
在这段代码中,我们展示了如何使用插入排序对一个整数数组进行排序。插入排序通过逐步将未排序部分的元素插入到已排序部分的正确位置来完成排序过程。