简单选择排序(Selection Sort)
1. 算法思想
它通过多次遍历数组,每次从未排序部分中选择最小(或最大)的元素,将其放到已排序部分的末尾(或开头),直到整个数组有序。
2. 示例
原始数组 :[64, 25, 12, 22, 11]
- 第1轮(从未排序的n个元素中查找) :
- 找到最小值
11
,与第1个元素64
交换。 - 结果:
[11, 25, 12, 22, 64]
- 找到最小值
- 第2轮(从未排序的n - 1个元素中查找) :
- 找到最小值
12
,与第2个元素25
交换。 - 结果:
[11, 12, 25, 22, 64]
- 找到最小值
- 第3轮 :
- 找到最小值
22
,与第3个元素25
交换。 - 结果:
[11, 12, 22, 25, 64]
- 找到最小值
- 第4轮 :
- 剩余部分已排序,无需交换。
最终结果 :[11, 12, 22, 25, 64]
4. 时间复杂度
- 最坏情况 :
O(n²)
(当数组完全逆序时)。 - 最好情况 :
O(n²)
(即使数组已排序,仍需比较所有元素)。 - 平均情况 :
O(n²)
。
5. 空间复杂度
- 原地排序 :
O(1)
,只需常数级别的额外空间。
6. 稳定性
- 不稳定 :当存在相等元素时,可能改变它们的相对顺序。比如L={2,
2
,1},第一趟排序:L={1,2
, 2},最终排序:L={1,2
, 2};可以看到元素两个2的顺序发生了变化。
8. 代码实现
c
void SelectSort(ElemType A[], int n) {
for (int i = 0; i < n; i++) {
int min = i;
//找到未排序元素中的最小值
for (int j = i + 1; j < n; j++)
if (A[j] < A[min]) min = i;
//将最小值放入已排序序列的末尾
if (min != i)
swap(A[i], A[min]);
}
}
堆排序(Heap Sort)
1.堆的概念
n个关键字序列L[1...n]称为堆,当且仅当满足:
- L(i) >= L(2i) 且 L(i) >= L(2 i+1)
此时为大顶堆
或 - L(i) <= L(2i) 且 L(i) <= L(2 i+1)
此时为小顶堆
2.创建堆(以大顶堆为例)
建堆步骤:
- 确定最后一个非叶结点的序号i = n/2
- 判断是否符合大顶堆规则
- 如果符合则对第i-1个结点从步骤二开始
- 如果不符合规则,则与较大的叶子结点交换,然后对其子树从步骤二开始
3.堆排序代码实现
堆排序分为两个主要阶段:建堆 和排序。
-
建堆:
- 将无序数组调整为大顶堆(或小顶堆)。
- 从最后一个非叶子节点开始,向上调整堆结构,确保每个子树满足堆的性质。
-
排序:
- 将堆顶元素(最大值)与堆的最后一个元素交换,将最大值"移出"堆。
- 缩小堆的范围(排除已排序部分),对剩余部分重新调整为堆。
- 重复上述过程,直到堆为空。
c
void AdjustHeap(Element A[], int k, int len) {
A[0] = A[k];
for(int i = 2 * k; i <= len; i = i * 2){
if (i < len && A[i] < A[i + 1])
i++;
if (A[0] >= A[i]) break;
else {
A[k] = A[i];
k = i;
}
}
A[k] = A[0];
}
void BuildHeap(Element A[], int len) {
for (int i = len / 2; i >= 1; i--) {
AdjustHeap(A, i, len);
}
}
void HeapSort(Element A[], int len) {
BuildHeap(A, len);
for (int i = len; i > 1; i--) {
Swap(A[i], A[1]);
AdjustHeap(A, 1, i - 1);
}
}
当从大量数据中选取前k大或前k小的元素时,堆排序可以表现出较优的时间复杂度。
4. 时间复杂度
- 建堆 :
O(n)
- 排序 :每次调整堆的时间复杂度为
O(log n)
,需要调整n-1
次。 - 总时间复杂度 :
O(n log n)
5. 空间复杂度
- 原地排序 :
O(1)
,只需常数级别的额外空间。
6. 稳定性
- 不稳定:堆排序可能改变相等元素的相对顺序。可以以{1,2,2}将其调整为大顶堆为例。