排序——内部排序之选择排序
排序——内部排序之选择排序
简单选择排序
选择排序开始的时候,我们扫描整个列表,找到它的最小元素然后和第一个元素交换,将最小元素放到它在有序表的最终位置上。然后我们从第二个元素开始扫描列表,找到最后n-1个元素的最小元素,再和第二个元素交换位置,把第二小的元素放在它最终的位置上。如此循环下去,在n-1遍以后,列表就排好序了。
算法:
```cpp
void SelectSort(ElemType A[], int n) {
int min = 0;
for (int i = 0; i < n-1; i++) {
min = i;
for (int j = i + 1; j < n, j++) {
if (A[j] < A[min])
min = j;
}
if (min != i)
Swap(A[i], A[min]);
}
}
```
时间复杂度为O(n^2),空间复杂度为O(1)。
堆排序
1. 堆排序简介: 堆排序是一种树形选择排序的方法,它的特点是:在排序过程中,将L[1…n]看成一棵完全二叉树的顺序存储结构,利用完全二叉树的双亲节点和孩子节点的内在关系,在当前无序区中选择关键字最大的元素。
2. 堆的定义为: 堆分为最大堆和最小堆,其实就是完全二叉树。最大堆要求节点的元素都要不小于其孩子,最小堆要求节点元素都不大于其左右孩子,左右孩子节点之间元素大小不存在大小关系。
3. 基本思想(大根堆):
(1)构造初始堆:
是一个反复筛选的过程,n个节点的完全二叉树,最后一个节点是[n/2]个节点的孩子节点,对[n/2]个节点为根的子树进行筛选,使得该子树成为堆,之后一次向前对各节点[n/2-1…1]为根的子树进行筛选,如果不是堆交换后可能会破坏下一级堆,于是继续采用上述方法构造下一级堆,直到以该节点为根的子树构成堆为止,反复利用这个过程直到根节点。
建立大根堆的算法如下所示:
void BuildMaxHeap(ElemType A[], int len) {
// 数组下标是从1开始的
for (int i = len/2; i > 0; i--)
AdjustDown(A, i, len);
}
void AdjustDown(ElemType A[], int k, int len) {
// 将元素从k往下进行调整
A[0] = A[k]; // A[0]暂存元素
for (int i = 2 * k; i <= len; i *= 2) {
// 沿着k较大的子节点向下筛选
if (i < len && A[i] < A[i+1])
i++;
if(A[0] > A[i]) {
break;
} else {
A[k] = A[i]; // 将子节点的元素调整到双亲节点上;
k = i; // 从子节点开始继续往下筛选
}
}
A[i] = A[0];
}
向下调整的时间与树高度h有关,为O(h),建堆的过程中每次向下调整时,大部分节点的高度都比较小,因此可以证明在元素为n个的序列上建堆,其时间复杂度为O(n),说明可以在线性时间内建立一个堆。
(2)排序思路:
将初始待排序关键字序列(R1,R2…Rn)构建成大顶堆,此堆为初始的无序区
将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,…Rn-1)和新的有序区(Rn),由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,…Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2…Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
// 排序算法
void HeapSort(ElemType A[], int len) {
BuildMaxHeap(A, len);
for (int i = len; i > 1; i--) {
Swap(A[i], A[1]); // 输出堆顶元素
AdjustDown(A, 1, i-1); // 把剩余的i-1个元素整理成堆。
}
}
4. 堆的插入和删除
(1)堆的删除: 由于堆的堆顶元素很有可能是最大的或者是最小的,所以在删除堆顶元素的时候先将堆顶元素与最后一个元素交换,在从堆顶开始向下的调整操作。
**(2)堆的插入:**堆的插入操作将元素插入到末尾,再对新节点进行向上的调整操作,向上调整的算法如下所示:
void AdjustUp(ElemType A[], int k, int len) {
A[0] = A[k];
for (int i = k / 2; i > 0; i = k / 2) {
if (A[i] < A[0]) {
A[k] = A[i];
k = i;
}
}
A[k] = A[0]
}
5. 时空复杂度
空间复杂度为O(1), 时间复杂度:建堆时间为O(n),排序的时候向下调整的时间复杂度为O(h),所以时间复杂度为O(nlogn).堆排序不是稳定的排序算法。